Merge SOYC report branch into trunk.
Patch by: bobv, kprobst
Review by: kprobst, spoon, bobv
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4218 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java
index f2b3cd3..a45e34c 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/AbstractLinker.java
@@ -38,7 +38,7 @@
*/
protected final SyntheticArtifact emitBytes(TreeLogger logger, byte[] what,
String partialPath) throws UnableToCompleteException {
- return new SyntheticArtifact(getClass(), partialPath, what);
+ return new SyntheticArtifact(logger, getClass(), partialPath, what);
}
/**
@@ -54,7 +54,8 @@
InputStream what, String partialPath) throws UnableToCompleteException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Util.copy(logger, what, out);
- return new SyntheticArtifact(getClass(), partialPath, out.toByteArray());
+ return new SyntheticArtifact(logger, getClass(), partialPath,
+ out.toByteArray());
}
/**
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java b/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java
new file mode 100644
index 0000000..b5b75d2
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/CompilationAnalysis.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.linker;
+
+import com.google.gwt.core.ext.Linker;
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.FunctionMember;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.core.ext.soyc.Story;
+
+import java.util.SortedSet;
+
+/**
+ * Represents analysis data for a CompilationResult.
+ */
+public abstract class CompilationAnalysis extends Artifact<CompilationAnalysis> {
+
+ /**
+ * Associates a Story and a Range of the output. Instances of this interface
+ * are obtained from {@link CompilationAnalysis#getSnippets()}.
+ */
+ public interface Snippet {
+ Range getRange();
+
+ Story getStory();
+ }
+
+ protected CompilationAnalysis(Class<? extends Linker> linkerType) {
+ super(linkerType);
+ }
+
+ /**
+ * Returns all ClassMembers present in the CompilationResult. This method
+ * would typically be used by consumers that are interested in the type
+ * hierarchy of the compilation.
+ */
+ public abstract SortedSet<ClassMember> getClasses();
+
+ /**
+ * Returns the CompilationResult upon which the analysis was performed.
+ */
+ public abstract CompilationResult getCompilationResult();
+
+ /**
+ * Returns all JavaScript FunctionMembers in the output.
+ */
+ public abstract SortedSet<FunctionMember> getFunctions();
+
+ /**
+ * Provides access to the assignments of Stories to Ranges for a fragment of
+ * the output. The Ranges are guaranteed not to overlap, and may be used for
+ * exact accounting of bytes. Due to the potential for very large data-sets to
+ * be accessible through this method, it is recommended that Snippets should
+ * be processed in an incremental fashion that does not require all instances
+ * to be retained at once.
+ */
+ /*
+ * NB: The reason that this returns an Iterable, and not a Map, is that we
+ * want to delay the construction of Range objects for as long as possible. If
+ * we were to return a Map for an analysis of N stories, we would also need N
+ * Ranges, plus the overhead of constructing an ordered Map.
+ */
+ public abstract Iterable<Snippet> getSnippets(int fragmentNumber);
+
+ /**
+ * Returns all Stories.
+ */
+ public abstract SortedSet<Story> getStories();
+
+ @Override
+ public final int hashCode() {
+ // NB: Identity is keyed to the CompilationResult
+ return getCompilationResult().hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "Compilation analysis for " + getCompilationResult().toString();
+ }
+
+ @Override
+ protected final int compareToComparableArtifact(CompilationAnalysis o) {
+ /*
+ * The identity of a CompilationAnalysis is based on the identity of its
+ * associated CompilationResult.
+ */
+ return getCompilationResult().compareToComparableArtifact(
+ o.getCompilationResult());
+ }
+
+ @Override
+ protected final Class<CompilationAnalysis> getComparableArtifactType() {
+ return CompilationAnalysis.class;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java b/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java
index 5e93e34..eed6c6a 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/SyntheticArtifact.java
@@ -18,26 +18,44 @@
import com.google.gwt.core.ext.Linker;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.util.Util;
-import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.io.InputStream;
/**
* Artifacts created by {@link AbstractLinker}.
*/
public class SyntheticArtifact extends EmittedArtifact {
- private final byte[] data;
+ private final File backing;
- SyntheticArtifact(Class<? extends Linker> linkerType, String partialPath,
- byte[] data) {
+ SyntheticArtifact(TreeLogger logger, Class<? extends Linker> linkerType,
+ String partialPath, byte[] data) throws UnableToCompleteException {
super(linkerType, partialPath);
assert data != null;
- this.data = data;
+
+ try {
+ backing = File.createTempFile("synthetic", ".artifact");
+ backing.deleteOnExit();
+ Util.writeBytesToFile(TreeLogger.NULL, backing, data);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to write backing file for artifact "
+ + partialPath, e);
+ throw new UnableToCompleteException();
+ }
}
@Override
public InputStream getContents(TreeLogger logger)
throws UnableToCompleteException {
- return new ByteArrayInputStream(data);
+ try {
+ return new FileInputStream(backing);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to read backing file for artifact "
+ + getPartialPath(), e);
+ throw new UnableToCompleteException();
+ }
}
}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
index 3d78cb9..d13c0218 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/SelectionScriptLinker.java
@@ -40,7 +40,7 @@
/**
* A base class for Linkers that use an external script to boostrap the GWT
- * module. This implementation injects JavaScript snippits into a JS program
+ * module. This implementation injects JavaScript Snippets into a JS program
* defined in an external file.
*/
public abstract class SelectionScriptLinker extends AbstractLinker {
@@ -295,7 +295,7 @@
}
/**
- * Generate a snippit of JavaScript to inject an external stylesheet.
+ * Generate a Snippet of JavaScript to inject an external stylesheet.
*
* <pre>
* if (!__gwt_stylesLoaded['URL']) {
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java
new file mode 100644
index 0000000..a1fbf06
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationAnalysis.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.linker.impl;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.CompilationAnalysis;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.FunctionMember;
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.core.ext.soyc.Story;
+import com.google.gwt.core.ext.soyc.Story.Origin;
+import com.google.gwt.core.ext.soyc.impl.AbstractMemberWithDependencies;
+import com.google.gwt.core.ext.soyc.impl.MemberFactory;
+import com.google.gwt.core.ext.soyc.impl.OriginImpl;
+import com.google.gwt.core.ext.soyc.impl.SnippetIterator;
+import com.google.gwt.core.ext.soyc.impl.StandardClassMember;
+import com.google.gwt.core.ext.soyc.impl.StandardFieldMember;
+import com.google.gwt.core.ext.soyc.impl.StandardFunctionMember;
+import com.google.gwt.core.ext.soyc.impl.StandardMethodMember;
+import com.google.gwt.core.ext.soyc.impl.StoryImpl;
+import com.google.gwt.dev.jjs.Correlation;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.Correlation.Axis;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.js.ast.JsFunction;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.Stack;
+import java.util.TreeSet;
+
+/**
+ * An implementation of CompilationAnalysis. This class transforms SourceInfos
+ * and related data into an API suitable for public consumption via the Linker
+ * API.
+ */
+public class StandardCompilationAnalysis extends CompilationAnalysis {
+ /**
+ * A roll-up struct for all the data produced by the analysis to make
+ * serialization simpler.
+ */
+ private static class Data implements Serializable {
+ SortedSet<ClassMember> classes;
+ SortedSet<FunctionMember> functions;
+
+ /**
+ * These are the Stories in the order in which they should be presented to
+ * the user via {@link CompilationAnalysis#getSnippets()}.
+ */
+ Map<Integer, List<StoryImpl>> orderedStories = new HashMap<Integer, List<StoryImpl>>();
+
+ SortedSet<Story> stories;
+ }
+
+ /**
+ * Associates a SourceInfo with a Range.
+ */
+ private static class RangeInfo {
+ public final SourceInfo info;
+ public final Range range;
+
+ public RangeInfo(Range range, SourceInfo info) {
+ this.range = range;
+ this.info = info;
+ }
+ }
+
+ private Data data;
+
+ /**
+ * Used by {@link #popAndRecord(Stack)} to determine start and end ranges.
+ */
+ private int lastEnd = 0;
+
+ private CompilationResult result;
+
+ /**
+ * This is a class field for convenience, but it should be deleted at the end
+ * of the constructor.
+ */
+ private transient Map<SourceInfo, StoryImpl> storyCache = new IdentityHashMap<SourceInfo, StoryImpl>();
+
+ /**
+ * This is a class field for convenience, but it should be deleted at the end
+ * of the constructor.
+ */
+ private transient Map<Correlation, Member> membersByCorrelation = new IdentityHashMap<Correlation, Member>();
+
+ /**
+ * Constructed by PermutationCompiler.
+ */
+ public StandardCompilationAnalysis(TreeLogger logger,
+ List<Map<Range, SourceInfo>> sourceInfoMaps)
+ throws UnableToCompleteException {
+ super(StandardLinkerContext.class);
+ logger = logger.branch(TreeLogger.INFO,
+ "Creating CompilationAnalysis (this may take some time)");
+
+ data = new Data();
+
+ /*
+ * Don't retain beyond the constructor to avoid lingering references to AST
+ * nodes.
+ */
+ MemberFactory memberFactory = new MemberFactory();
+
+ // Record what we've seen so far
+ TreeSet<ClassMember> classesMutable = new TreeSet<ClassMember>(
+ Member.SOURCE_NAME_COMPARATOR);
+ TreeSet<FunctionMember> functionsMutable = new TreeSet<FunctionMember>(
+ Member.SOURCE_NAME_COMPARATOR);
+ Set<SourceInfo> sourceInfoSeen = new HashSet<SourceInfo>();
+
+ int fragment = 0;
+ for (Map<Range, SourceInfo> sourceInfoMap : sourceInfoMaps) {
+ lastEnd = 0;
+ analyzeFragment(memberFactory, classesMutable, functionsMutable,
+ sourceInfoMap, sourceInfoSeen, fragment++);
+ }
+
+ data.classes = Collections.unmodifiableSortedSet(classesMutable);
+ data.functions = Collections.unmodifiableSortedSet(functionsMutable);
+
+ // Deduplicate the ordered stories into an ordered set
+ SortedSet<Story> mutableStories = new TreeSet<Story>(
+ StoryImpl.ID_COMPARATOR);
+ for (List<StoryImpl> stories : data.orderedStories.values()) {
+ mutableStories.addAll(stories);
+ }
+ data.stories = Collections.unmodifiableSortedSet(mutableStories);
+
+ /*
+ * Clear the member fields that we don't need anymore to allow GC of the
+ * SourceInfo objects
+ */
+ membersByCorrelation = null;
+ storyCache = null;
+
+ logger.log(TreeLogger.INFO, "Done");
+ }
+
+ @Override
+ public SortedSet<ClassMember> getClasses() {
+ return data.classes;
+ }
+
+ @Override
+ public CompilationResult getCompilationResult() {
+ return result;
+ }
+
+ @Override
+ public SortedSet<FunctionMember> getFunctions() {
+ return data.functions;
+ }
+
+ @Override
+ public Iterable<Snippet> getSnippets(int fragment) {
+ final List<StoryImpl> stories = data.orderedStories.get(fragment);
+ if (stories == null) {
+ throw new IllegalArgumentException("Unknown fragment id " + fragment);
+ }
+
+ return new Iterable<Snippet>() {
+ public Iterator<Snippet> iterator() {
+ assert stories != null;
+ return new SnippetIterator(stories);
+ }
+ };
+ }
+
+ @Override
+ public SortedSet<Story> getStories() {
+ return data.stories;
+ }
+
+ /**
+ * Back-channel setter used by PermutationCompiler.
+ */
+ public void setCompilationResult(CompilationResult result) {
+ this.result = result;
+ }
+
+ private void analyzeFragment(MemberFactory memberFactory,
+ TreeSet<ClassMember> classesMutable,
+ TreeSet<FunctionMember> functionsMutable,
+ Map<Range, SourceInfo> sourceInfoMap, Set<SourceInfo> sourceInfoSeen,
+ int fragment) {
+ /*
+ * We want to iterate over the Ranges so that enclosing Ranges come before
+ * their enclosed Ranges...
+ */
+ Range[] dependencyOrder = sourceInfoMap.keySet().toArray(
+ new Range[sourceInfoMap.size()]);
+ Arrays.sort(dependencyOrder, Range.DEPENDENCY_ORDER_COMPARATOR);
+
+ Stack<RangeInfo> dependencyScope = new Stack<RangeInfo>();
+ for (Range range : dependencyOrder) {
+ SourceInfo info = sourceInfoMap.get(range);
+ assert info != null;
+
+ // Infer dependency information
+ if (!dependencyScope.isEmpty()) {
+
+ /*
+ * Pop frames until we get back to a container, using this as a chance
+ * to build up our list of non-overlapping Ranges to report back to the
+ * user.
+ */
+ while (!dependencyScope.peek().range.contains(range)) {
+ popAndRecord(dependencyScope, fragment);
+ }
+ }
+
+ // Possibly create and record Members
+ if (!sourceInfoSeen.contains(info)) {
+ sourceInfoSeen.add(info);
+ for (Correlation c : info.getPrimaryCorrelations()) {
+ if (membersByCorrelation.containsKey(c)) {
+ continue;
+ }
+
+ switch (c.getAxis()) {
+ case CLASS: {
+ JReferenceType type = c.getType();
+ StandardClassMember member = memberFactory.get(type);
+ membersByCorrelation.put(c, member);
+ classesMutable.add(member);
+ break;
+ }
+ case FIELD: {
+ JField field = c.getField();
+ JReferenceType type = c.getType();
+ StandardFieldMember member = memberFactory.get(field);
+ memberFactory.get(type).addField(member);
+ membersByCorrelation.put(c, member);
+ break;
+ }
+ case FUNCTION: {
+ JsFunction function = c.getFunction();
+ StandardFunctionMember member = memberFactory.get(function);
+ membersByCorrelation.put(c, member);
+ functionsMutable.add(member);
+ break;
+ }
+ case METHOD: {
+ JMethod method = c.getMethod();
+ JReferenceType type = c.getType();
+ StandardMethodMember member = memberFactory.get(method);
+ memberFactory.get(type).addMethod(member);
+ membersByCorrelation.put(c, member);
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * Record dependencies as observed in the structure of the JS output. This
+ * an an ad-hoc approach that just looks at which SourceInfos are used
+ * within the Range of another SourceInfo.
+ */
+ Set<Correlation> correlationsInScope = new HashSet<Correlation>();
+ for (RangeInfo outer : dependencyScope) {
+ SourceInfo outerInfo = outer.info;
+ correlationsInScope.addAll(outerInfo.getPrimaryCorrelations());
+
+ for (Correlation outerCorrelation : outerInfo.getPrimaryCorrelations()) {
+ Member outerMember = membersByCorrelation.get(outerCorrelation);
+
+ if (outerMember instanceof AbstractMemberWithDependencies) {
+ for (Correlation innerCorrelation : info.getAllCorrelations()) {
+ /*
+ * This check prevents an inlined method from depending on the
+ * method or function into which is was inlined.
+ */
+ if (correlationsInScope.contains(innerCorrelation)) {
+ continue;
+ }
+
+ Member innerMember = membersByCorrelation.get(innerCorrelation);
+
+ /*
+ * The null check is because we may not create Members for all
+ * types of Correlations.
+ */
+ if (innerMember != null) {
+ if (((AbstractMemberWithDependencies) outerMember).addDependency(innerMember)) {
+ // System.out.println(outerMember + " -> " + innerMember);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ dependencyScope.push(new RangeInfo(range, info));
+ }
+
+ // Unwind the rest of the stack to finish out the ranges
+ while (!dependencyScope.isEmpty()) {
+ popAndRecord(dependencyScope, fragment);
+ }
+
+ /*
+ * Because the first Range corresponds to the SourceInfo of the whole
+ * program, we'll know that we got all of the data if the ends match up. If
+ * this assert passes, we know that we've correctly generated a sequence of
+ * non-overlapping Ranges that encompass the whole program.
+ */
+ assert dependencyOrder[0].getEnd() == lastEnd;
+ }
+
+ /**
+ * Remove an element from the RangeInfo stack and stare a new StoryImpl with
+ * the right length, possibly sub-dividing the super-enclosing Range in the
+ * process.
+ */
+ private void popAndRecord(Stack<RangeInfo> dependencyScope, int fragment) {
+ RangeInfo rangeInfo = dependencyScope.pop();
+ Range toStore = rangeInfo.range;
+
+ /*
+ * Make a new Range for the gap between the popped Range and whatever we
+ * last stored.
+ */
+ if (lastEnd < toStore.getStart()) {
+ Range newRange = new Range(lastEnd, toStore.getStart());
+ assert !dependencyScope.isEmpty();
+
+ SourceInfo gapInfo = dependencyScope.peek().info;
+ recordStory(gapInfo, fragment, newRange.length());
+ lastEnd += newRange.length();
+ }
+
+ /*
+ * Store as much of the current Range as we haven't previously stored. The
+ * Max.max() is there to take care of the tail end of Ranges that have had a
+ * sub-range previously stored.
+ */
+ if (lastEnd < toStore.getEnd()) {
+ Range newRange = new Range(Math.max(lastEnd, toStore.getStart()),
+ toStore.getEnd());
+ recordStory(rangeInfo.info, fragment, newRange.length());
+ lastEnd += newRange.length();
+ }
+ }
+
+ private void recordStory(SourceInfo info, int fragment, int length) {
+ assert storyCache != null;
+
+ StoryImpl theStory;
+ if (!storyCache.containsKey(info)) {
+
+ SortedSet<Member> members = new TreeSet<Member>(
+ Member.TYPE_AND_SOURCE_NAME_COMPARATOR);
+
+ if (info != null) {
+ for (Correlation c : info.getAllCorrelations()) {
+ Member m = membersByCorrelation.get(c);
+ if (m != null) {
+ members.add(m);
+ }
+ }
+ }
+
+ SortedSet<Origin> origins = new TreeSet<Origin>();
+ for (Correlation c : info.getAllCorrelations(Axis.ORIGIN)) {
+ origins.add(new OriginImpl(c.getOrigin()));
+ }
+
+ String literalType = null;
+ Correlation literalCorrelation = info.getPrimaryCorrelation(Axis.LITERAL);
+ if (literalCorrelation != null) {
+ literalType = literalCorrelation.getLiteral().getDescription();
+ }
+
+ theStory = new StoryImpl(storyCache.size(), members, info.getMutations(),
+ origins, literalType, fragment, length);
+ storyCache.put(info, theStory);
+ } else {
+ // Use a copy-constructed instance
+ theStory = new StoryImpl(storyCache.get(info), length);
+ }
+
+ List<StoryImpl> stories = data.orderedStories.get(fragment);
+ if (stories == null) {
+ stories = new ArrayList<StoryImpl>();
+ data.orderedStories.put(fragment, stories);
+ }
+ stories.add(theStory);
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
index 716995e..bf4306b 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardCompilationResult.java
@@ -15,12 +15,13 @@
*/
package com.google.gwt.core.ext.linker.impl;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.CompilationResult;
import com.google.gwt.core.ext.linker.SelectionProperty;
import com.google.gwt.dev.PermutationResult;
-import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.FileBackedObject;
-import java.io.File;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.Collections;
@@ -69,14 +70,15 @@
*/
public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new MapComparator();
- private final File resultFile;
+ private final FileBackedObject<PermutationResult> resultFile;
private transient SoftReference<String[]> js;
private final SortedSet<SortedMap<SelectionProperty, String>> propertyValues = new TreeSet<SortedMap<SelectionProperty, String>>(
MAP_COMPARATOR);
private final String strongName;
- public StandardCompilationResult(String[] js, String strongName, File resultFile) {
+ public StandardCompilationResult(String[] js, String strongName,
+ FileBackedObject<PermutationResult> resultFile) {
super(StandardLinkerContext.class);
this.js = new SoftReference<String[]>(js);
this.strongName = strongName;
@@ -104,19 +106,13 @@
if (toReturn == null) {
PermutationResult result;
try {
- result = Util.readFileAsObject(resultFile, PermutationResult.class);
- } catch (ClassNotFoundException e) {
+ result = resultFile.newInstance(TreeLogger.NULL);
+ toReturn = result.getJs();
+ js = new SoftReference<String[]>(toReturn);
+ } catch (UnableToCompleteException e) {
throw new RuntimeException(
- "Unexpectedly unable to read PermutationResult '"
- + resultFile.getAbsolutePath() + "'", e);
+ "Unexpectedly unable to read PermutationResult");
}
- if (result == null) {
- throw new RuntimeException(
- "Unexpectedly unable to read PermutationResult '"
- + resultFile.getAbsolutePath() + "'");
- }
- toReturn = result.getJs();
- js = new SoftReference<String[]>(toReturn);
}
return toReturn;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
index cb9619a..1a4e649 100644
--- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
+++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardLinkerContext.java
@@ -51,6 +51,7 @@
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsScope;
import com.google.gwt.dev.util.DefaultTextOutput;
+import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.Util;
import java.io.File;
@@ -263,20 +264,9 @@
* Gets or creates a CompilationResult for the given JavaScript program.
*/
public StandardCompilationResult getCompilation(TreeLogger logger,
- File resultFile) throws UnableToCompleteException {
- PermutationResult permutationResult;
- try {
- permutationResult = Util.readFileAsObject(resultFile,
- PermutationResult.class);
- } catch (ClassNotFoundException e) {
- logger.log(TreeLogger.ERROR, "Unable to instantiate PermutationResult", e);
- throw new UnableToCompleteException();
- }
-
- if (permutationResult == null) {
- logger.log(TreeLogger.ERROR, "Unable to read PermutationResult");
- throw new UnableToCompleteException();
- }
+ FileBackedObject<PermutationResult> resultFile)
+ throws UnableToCompleteException {
+ PermutationResult permutationResult = resultFile.newInstance(logger);
String strongName = Util.computeStrongName(Util.getBytes(permutationResult.getJs()));
StandardCompilationResult result = resultsByStrongName.get(strongName);
@@ -285,6 +275,16 @@
strongName, resultFile);
resultsByStrongName.put(result.getStrongName(), result);
artifacts.add(result);
+
+ // Add any other Permutations
+ ArtifactSet otherArtifacts = permutationResult.getArtifacts();
+ if (otherArtifacts != null) {
+ // Fixups for StandardCompilationAnalysis objects
+ for (StandardCompilationAnalysis a : otherArtifacts.find(StandardCompilationAnalysis.class)) {
+ a.setCompilationResult(result);
+ }
+ artifacts.addAll(otherArtifacts);
+ }
}
return result;
}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/ClassMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/ClassMember.java
new file mode 100644
index 0000000..e8e84b8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/ClassMember.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.util.SortedSet;
+
+/**
+ * Represents a reference type, such as a class or interface, in the compiled
+ * output. Methods and fields of the original Java type will have been pruned by
+ * the compiler, so the values returned by {@link #getFields()} and
+ * {@link #getMethods()} may be incomplete when compared to the original Java
+ * type.
+ */
+public interface ClassMember extends HasDependencies,
+ HasOverrides<ClassMember>, Member {
+ /**
+ * Returns the fields of the ClassMember that have been retained in the
+ * compiled output.
+ */
+ SortedSet<FieldMember> getFields();
+
+ /**
+ * Returns the methods of the ClassMember that have been retained in the
+ * compiled output.
+ */
+ SortedSet<MethodMember> getMethods();
+
+ /**
+ * Returns the Java package from which the ClassMember originated.
+ */
+ String getPackage();
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/FieldMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/FieldMember.java
new file mode 100644
index 0000000..a431f86
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/FieldMember.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+/**
+ * Represents a field in a Java type.
+ */
+public interface FieldMember extends HasEnclosing, Member {
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/FunctionMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/FunctionMember.java
new file mode 100644
index 0000000..574a292
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/FunctionMember.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ *
+ */
+package com.google.gwt.core.ext.soyc;
+
+/**
+ * Represents a JavaScript function in the compiled output. Due to compiler
+ * optimizations, there may be arbitrary relationships between FunctionMembers
+ * and MethodMembers.
+ */
+public interface FunctionMember extends HasDependencies, Member {
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/HasAliases.java b/dev/core/src/com/google/gwt/core/ext/soyc/HasAliases.java
new file mode 100644
index 0000000..354aff4
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/HasAliases.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.util.SortedSet;
+
+/**
+ * A tag interface for Members to indicate that there may be additional,
+ * possibly non-unique, names in the compiled output that refer to the member.
+ */
+public interface HasAliases {
+ /**
+ * Returns any aliases used in the compiled output that refer to this
+ * MethodMember. Note that especially in the case of
+ * polymorphically-dispatched methods, there may be many MethodMembers with
+ * overlapping aliases.
+ */
+ SortedSet<String> getJsAliases();
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/HasDependencies.java b/dev/core/src/com/google/gwt/core/ext/soyc/HasDependencies.java
new file mode 100644
index 0000000..637dc52
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/HasDependencies.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ *
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.util.SortedSet;
+
+/**
+ * A tag interface for Members to indicate that they have a static dependency on
+ * another Member.
+ */
+public interface HasDependencies {
+ SortedSet<Member> getDependencies();
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/HasEnclosing.java b/dev/core/src/com/google/gwt/core/ext/soyc/HasEnclosing.java
new file mode 100644
index 0000000..d6e1589
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/HasEnclosing.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ *
+ */
+package com.google.gwt.core.ext.soyc;
+
+/**
+ * A tag interface for Members to indicate that they have an enclosing
+ * ClassMember.
+ */
+public interface HasEnclosing {
+ ClassMember getEnclosing();
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/HasOverrides.java b/dev/core/src/com/google/gwt/core/ext/soyc/HasOverrides.java
new file mode 100644
index 0000000..5d6001b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/HasOverrides.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ *
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.util.SortedSet;
+
+/**
+ * A tag interface for Members to indicate that they override or extend another
+ * Member.
+ *
+ * @param <T> the type of Member that is overridden or extended
+ */
+public interface HasOverrides<T extends Member> {
+ /**
+ * Returns the complete set of overridden members. For example, if
+ * <code>C</code> extends <code>B</code> extends <code>A</code>, then
+ * <code>C</code> would return the set <code>[A, B]</code>.
+ */
+ SortedSet<T> getOverrides();
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/Member.java b/dev/core/src/com/google/gwt/core/ext/soyc/Member.java
new file mode 100644
index 0000000..8a2b64e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/Member.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * The Member type hierarchy represents structural or logical structures in the
+ * compiled output. Members don't have a getRange() function because the bytes
+ * derived from the member are likely disjoint.
+ */
+public interface Member extends Serializable {
+
+ /**
+ * Compares Members based solely on source name. This comparator is faster
+ * than {@link #TYPE_AND_SOURCE_NAME_COMPARATOR}, but is only appropriate for
+ * use with homogeneous collections of Members.
+ */
+ Comparator<Member> SOURCE_NAME_COMPARATOR = new SourceNameComparator();
+
+ /**
+ * Compares Members based on type and source name.
+ */
+ Comparator<Member> TYPE_AND_SOURCE_NAME_COMPARATOR = new TypeAndSourceNameComparator();;
+
+ /**
+ * Returns the (possibly obfuscated) identifier used in the output.
+ */
+ String getJsName();
+
+ /**
+ * Returns a description of where the source for the Member originated.
+ * Usually, but not always, a URL.
+ */
+ String getSourceLocation();
+
+ /**
+ * Returns the name of the Member in the original source code.
+ */
+ String getSourceName();
+
+ /**
+ * Returns the Member if it is a ClassMember or <code>null</code>.
+ */
+ ClassMember isClass();
+
+ /**
+ * Returns the Member if it is a FieldMember or <code>null</code>.
+ */
+ FieldMember isField();
+
+ /**
+ * Returns the Member if it is a FunctionMember or <code>null</code>.
+ */
+ FunctionMember isFunction();
+
+ /**
+ * Returns the Member if it is a MethodMember or <code>null</code>.
+ */
+ MethodMember isMethod();
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/MethodMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/MethodMember.java
new file mode 100644
index 0000000..9966890
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/MethodMember.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ *
+ */
+package com.google.gwt.core.ext.soyc;
+
+/**
+ * Represents compiled JS code derived from a Java method.
+ */
+public interface MethodMember extends HasAliases, HasDependencies,
+ HasEnclosing, HasOverrides<MethodMember>, Member {
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/Range.java b/dev/core/src/com/google/gwt/core/ext/soyc/Range.java
new file mode 100644
index 0000000..8414263
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/Range.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/**
+ *
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.util.Comparator;
+
+/**
+ * Represents a contiguous region of characters in the compiler output.
+ */
+public final class Range {
+ /**
+ * Sorts Ranges so that a Range will be preceeded by any Ranges that enclose
+ * it.
+ */
+ public static final Comparator<Range> DEPENDENCY_ORDER_COMPARATOR = new Comparator<Range>() {
+ public int compare(Range o1, Range o2) {
+ int a = o1.start - o2.start;
+ if (a != 0) {
+ return a;
+ }
+
+ return o2.end - o1.end;
+ }
+ };
+
+ /**
+ * Sorts Ranges into the order in which they would appear in the source code
+ * based on start position and end position.
+ */
+ public static final Comparator<Range> SOURCE_ORDER_COMPARATOR = new Comparator<Range>() {
+ public int compare(Range o1, Range o2) {
+ int a = o1.start - o2.start;
+ if (a != 0) {
+ return a;
+ }
+
+ return o1.end - o2.end;
+ }
+ };
+
+ private final int end;
+ private final int start;
+
+ /**
+ * Constructor.
+ *
+ * @param start must be non-negative
+ * @param end must be greater than or equal to <code>start</code>
+ */
+ public Range(int start, int end) {
+ assert start >= 0;
+ assert start <= end;
+ this.start = start;
+ this.end = end;
+ }
+
+ /**
+ * Return <code>true</code> if the given Range lies wholly within the Range.
+ */
+ public boolean contains(Range o) {
+ return start <= o.start && o.end <= end;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Range)) {
+ return false;
+ }
+ Range o = (Range) obj;
+ return start == o.start && end == o.end;
+ }
+
+ public int getEnd() {
+ return end;
+ }
+
+ public int getStart() {
+ return start;
+ }
+
+ @Override
+ public int hashCode() {
+ return 37 * start + end;
+ }
+
+ public int length() {
+ return end - start;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return "[" + start + " - " + end + ")";
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/SourceNameComparator.java b/dev/core/src/com/google/gwt/core/ext/soyc/SourceNameComparator.java
new file mode 100644
index 0000000..19cd368
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/SourceNameComparator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * This is a top-level type so that we can serialize any TreeMaps that happen to
+ * use {@link Member#SOURCE_NAME_COMPARATOR}.
+ */
+class SourceNameComparator implements Comparator<Member>, Serializable {
+ public int compare(Member o1, Member o2) {
+ return o1.getSourceName().compareTo(o2.getSourceName());
+ }
+
+ /**
+ * Always use the singleton instance.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ return Member.SOURCE_NAME_COMPARATOR;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/Story.java b/dev/core/src/com/google/gwt/core/ext/soyc/Story.java
new file mode 100644
index 0000000..a08fd85
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/Story.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * Represents a (possibly disjoint) region of the JavaScript output for which
+ * metadata is available.
+ */
+public interface Story extends Serializable {
+ /*
+ * Corresponds to a SourceInfo.
+ */
+
+ /**
+ * Describes the physical or virtual source location from which a Story
+ * originated.
+ */
+ public interface Origin extends Serializable {
+ /*
+ * Corresponds to a SourceOrigin.
+ */
+
+ /**
+ * Returns the line number, or <code>0</code> if there is no physical
+ * location.
+ */
+ int getLineNumber();
+
+ /**
+ * This is usually, but not always, a URL. If it is not a URL, it will
+ * typically be a Java class name.
+ */
+ String getLocation();
+ }
+
+ /**
+ * If the Story represents a literal value, this method will return a
+ * description of the type of literal. If the Story does not represent a
+ * literal, this method will return <code>null</code>.
+ */
+ // TODO: Consider promoting the Correlation.Literal enum to a public API
+ String getLiteralTypeName();
+
+ /**
+ * Gets the Members of the compilation that the Story is about.
+ */
+ SortedSet<Member> getMembers();
+
+ /**
+ * Gets a list of transformations applied to the original source code in order
+ * to produce the story entry. This method will not return any data unless the
+ * compiler has been configured to collect mutation data.
+ */
+ List<String> getMutations();
+
+ /**
+ * Returns the locations of the Story's source. Identical structures (such as
+ * string literals) that appear in multiple locations in the source may be
+ * merged by the compiler into a single Story.
+ */
+ SortedSet<Origin> getSourceOrigin();
+
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/TypeAndSourceNameComparator.java b/dev/core/src/com/google/gwt/core/ext/soyc/TypeAndSourceNameComparator.java
new file mode 100644
index 0000000..a78746d
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/TypeAndSourceNameComparator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * This is a top-level type so that we can serialize any TreeMaps that happen to
+ * use {@link Member#TYPE_AND_SOURCE_NAME_COMPARATOR}.
+ */
+class TypeAndSourceNameComparator implements Comparator<Member>, Serializable {
+ public int compare(Member o1, Member o2) {
+ int r = o1.getClass().getName().compareTo(o2.getClass().getName());
+ if (r != 0) {
+ return r;
+ }
+
+ return o1.getSourceName().compareTo(o2.getSourceName());
+ }
+
+ /**
+ * Always use the singleton instance.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ return Member.TYPE_AND_SOURCE_NAME_COMPARATOR;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/AbstractMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/AbstractMember.java
new file mode 100644
index 0000000..136dc93
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/AbstractMember.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.FieldMember;
+import com.google.gwt.core.ext.soyc.FunctionMember;
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.core.ext.soyc.MethodMember;
+import com.google.gwt.dev.jjs.Correlation;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.Correlation.Axis;
+
+/**
+ * Provides implementation of common Member functions.
+ */
+public abstract class AbstractMember implements Member {
+ private final String jsName;
+ private final String sourceLocation;
+
+ public AbstractMember(SourceInfo info) {
+ Correlation nameCorrelation = info.getPrimaryCorrelation(Axis.JS_NAME);
+ if (nameCorrelation != null) {
+ jsName = nameCorrelation.getName().getShortIdent();
+ } else {
+ jsName = null;
+ }
+ sourceLocation = info.getFileName();
+ }
+
+ public String getJsName() {
+ return jsName;
+ }
+
+ public String getSourceLocation() {
+ return sourceLocation;
+ }
+
+ public abstract String getSourceName();
+
+ public ClassMember isClass() {
+ return this instanceof ClassMember ? (ClassMember) this : null;
+ }
+
+ public FieldMember isField() {
+ return this instanceof FieldMember ? (FieldMember) this : null;
+ }
+
+ public FunctionMember isFunction() {
+ return this instanceof FunctionMember ? (FunctionMember) this : null;
+ }
+
+ public MethodMember isMethod() {
+ return this instanceof MethodMember ? (MethodMember) this : null;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/AbstractMemberWithDependencies.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/AbstractMemberWithDependencies.java
new file mode 100644
index 0000000..44fb5cf
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/AbstractMemberWithDependencies.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.HasDependencies;
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.dev.jjs.SourceInfo;
+
+import java.util.Collections;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Provides a common implementation of HasDependencies.
+ */
+public abstract class AbstractMemberWithDependencies extends AbstractMember
+ implements HasDependencies {
+ private final SortedSet<Member> dependencies = new TreeSet<Member>(
+ Member.TYPE_AND_SOURCE_NAME_COMPARATOR);
+ private final SortedSet<Member> dependenciesView = Collections.unmodifiableSortedSet(dependencies);
+
+ protected AbstractMemberWithDependencies(SourceInfo info) {
+ super(info);
+ }
+
+ /**
+ * Add a dependency.
+ *
+ * @return <code>true</code> if the dependency was not previously added.
+ */
+ public boolean addDependency(Member member) {
+ return dependencies.add(member);
+ }
+
+ public SortedSet<Member> getDependencies() {
+ return dependenciesView;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/MemberFactory.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/MemberFactory.java
new file mode 100644
index 0000000..273aa6b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/MemberFactory.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.dev.jjs.ast.JField;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+import com.google.gwt.dev.js.ast.JsFunction;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * A factory object for the standard implementations of Member subtypes. The
+ * factory methods in this type provide canonicalized instances. The maps used
+ * by MemberFactory use hard, identity-based references.
+ */
+public class MemberFactory {
+ private final Map<Class<?>, Map<?, ?>> map = new IdentityHashMap<Class<?>, Map<?, ?>>();
+
+ public StandardFieldMember get(JField field) {
+ return getOrCreate(field, StandardFieldMember.class, JField.class);
+ }
+
+ public StandardMethodMember get(JMethod method) {
+ return getOrCreate(method, StandardMethodMember.class, JMethod.class);
+ }
+
+ public StandardClassMember get(JReferenceType type) {
+ return getOrCreate(type, StandardClassMember.class, JReferenceType.class);
+ }
+
+ public StandardFunctionMember get(JsFunction function) {
+ return getOrCreate(function, StandardFunctionMember.class, JsFunction.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <K, V extends Member> Map<K, V> getElementMap(K key, Class<V> clazz) {
+ Map<K, V> elementMap = (Map<K, V>) map.get(clazz);
+ if (elementMap == null) {
+ elementMap = new IdentityHashMap<K, V>();
+ map.put(clazz, elementMap);
+ }
+ return elementMap;
+ }
+
+ /**
+ * Assumes that the implementation of Member has a two-arg constructor that
+ * accepts a MemberFactory and the key.
+ *
+ * @param <K> the type of key used to canonicalize the mapping
+ * @param <V> the type of Member implementation to use
+ * @param key the key by which the value should be canonicalized
+ * @param implClazz the concrete type of Member to construct
+ * @param constructorParam the declared type of the second parameter of the
+ * concrete Member type
+ * @return the canonicalized instance of Member for the given key
+ */
+ private <K, V extends Member> V getOrCreate(K key, Class<V> implClazz,
+ Class<? super K> constructorParam) {
+ Map<K, V> elementMap = getElementMap(key, implClazz);
+
+ V toReturn = elementMap.get(key);
+ if (toReturn == null) {
+ try {
+ Constructor<V> ctor = implClazz.getConstructor(MemberFactory.class,
+ constructorParam);
+ toReturn = ctor.newInstance(this, key);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(implClazz.getName()
+ + " must declare a two-arg (MemberFactory, "
+ + constructorParam.getName() + ") constructor", e);
+ } catch (IllegalArgumentException e) {
+ // Error on the part of this type
+ throw new RuntimeException(e);
+ } catch (InstantiationException e) {
+ // Error on the part of this type, asking for a non-instantiable type
+ throw new RuntimeException(e);
+ } catch (IllegalAccessException e) {
+ // Error on the part of the coder of implClazz
+ throw new RuntimeException(e);
+ } catch (InvocationTargetException e) {
+ // Probably a RuntimeException thrown from the constructor
+ if (e.getCause() instanceof RuntimeException) {
+ throw (RuntimeException) e.getCause();
+ }
+ throw new RuntimeException(e);
+ }
+
+ elementMap.put(key, toReturn);
+ }
+
+ return toReturn;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/OriginImpl.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/OriginImpl.java
new file mode 100644
index 0000000..76a56fe
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/OriginImpl.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.Story.Origin;
+import com.google.gwt.dev.jjs.SourceOrigin;
+
+/**
+ * An implementation of Origin, that initializes itself from a SourceOrigin.
+ */
+public class OriginImpl implements Origin, Comparable<OriginImpl> {
+
+ private final int lineNum;
+ private final String location;
+
+ public OriginImpl(SourceOrigin origin) {
+ this.location = origin.getFileName();
+ this.lineNum = origin.getStartLine();
+ }
+
+ public int compareTo(OriginImpl o) {
+ int a = location.compareTo(o.location);
+ if (a != 0) {
+ return a;
+ }
+ return lineNum - o.lineNum;
+ }
+
+ public int getLineNumber() {
+ return lineNum;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ @Override
+ public String toString() {
+ return location + " : " + lineNum;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/SnippetIterator.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SnippetIterator.java
new file mode 100644
index 0000000..7d2fb50
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/SnippetIterator.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.linker.CompilationAnalysis.Snippet;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.core.ext.soyc.Story;
+
+import java.util.Iterator;
+
+/**
+ * Uses a list of StoryImpls present a sequence of Snippets by synthesizing
+ * Range objects based on the length of the StoryImpls.
+ */
+public class SnippetIterator implements Iterator<Snippet> {
+ /**
+ * An Iterator over the backing object.
+ */
+ private final Iterator<StoryImpl> iter;
+
+ /**
+ * The starting position for the next Range object generated.
+ */
+ private int start = 0;
+
+ public SnippetIterator(Iterable<StoryImpl> stories) {
+ iter = stories.iterator();
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public Snippet next() {
+ final StoryImpl story = iter.next();
+ final Range range = new Range(start, start + story.getLength());
+ start += story.getLength();
+
+ return new Snippet() {
+ public Range getRange() {
+ return range;
+ }
+
+ public Story getStory() {
+ return story;
+ }
+ };
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardClassMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardClassMember.java
new file mode 100644
index 0000000..8a0a46f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardClassMember.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.FieldMember;
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.core.ext.soyc.MethodMember;
+import com.google.gwt.dev.jjs.ast.JReferenceType;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * An implementation of ClassMember. This implementation always returns
+ * unmodifiable collections since it is exposed directly to user code via the
+ * Linker API.
+ */
+public class StandardClassMember extends AbstractMemberWithDependencies
+ implements ClassMember {
+ private final SortedSet<FieldMember> fields = new TreeSet<FieldMember>(
+ Member.SOURCE_NAME_COMPARATOR);
+ private final SortedSet<FieldMember> fieldsView = Collections.unmodifiableSortedSet(fields);
+ private final SortedSet<MethodMember> methods = new TreeSet<MethodMember>(
+ Member.SOURCE_NAME_COMPARATOR);
+ private final SortedSet<MethodMember> methodsView = Collections.unmodifiableSortedSet(methods);
+ private final SortedSet<ClassMember> overridesView;
+ private final String packageName;
+ private final String sourceName;
+
+ /**
+ * Constructed by {@link MemberFactory#get(JReferenceType)}.
+ */
+ public StandardClassMember(MemberFactory factory, JReferenceType type) {
+ super(type.getSourceInfo());
+
+ int index = type.getName().lastIndexOf('.');
+ if (index < 0) {
+ packageName = "";
+ } else {
+ packageName = type.getName().substring(0, index).intern();
+ }
+ sourceName = type.getName().intern();
+
+ Set<JReferenceType> seen = new HashSet<JReferenceType>();
+ Set<JReferenceType> toTraverse = new HashSet<JReferenceType>();
+ toTraverse.add(type);
+
+ SortedSet<ClassMember> overrides = new TreeSet<ClassMember>(
+ Member.SOURCE_NAME_COMPARATOR);
+
+ while (!toTraverse.isEmpty()) {
+ JReferenceType currentType = toTraverse.iterator().next();
+ seen.add(currentType);
+
+ if (currentType != type) {
+ overrides.add(factory.get(currentType));
+ }
+
+ if (currentType.extnds != null) {
+ toTraverse.add(currentType.extnds);
+ }
+
+ if (currentType.implments != null) {
+ toTraverse.addAll(currentType.implments);
+ }
+
+ toTraverse.removeAll(seen);
+ }
+ overridesView = Collections.unmodifiableSortedSet(overrides);
+ }
+
+ public void addField(FieldMember field) {
+ fields.add(field);
+ }
+
+ public void addMethod(MethodMember method) {
+ methods.add(method);
+ }
+
+ public SortedSet<FieldMember> getFields() {
+ return fieldsView;
+ }
+
+ public SortedSet<MethodMember> getMethods() {
+ return methodsView;
+ }
+
+ public SortedSet<ClassMember> getOverrides() {
+ return overridesView;
+ }
+
+ public String getPackage() {
+ return packageName;
+ }
+
+ @Override
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return "ClassMember " + getSourceName();
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardFieldMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardFieldMember.java
new file mode 100644
index 0000000..e40185c
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardFieldMember.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.FieldMember;
+import com.google.gwt.dev.jjs.ast.JField;
+
+/**
+ * An implementation of FieldMember.
+ */
+public class StandardFieldMember extends AbstractMember implements FieldMember {
+ private final ClassMember enclosing;
+ private final String sourceName;
+
+ /**
+ * Constructed by {@link MemberFactory#get(JFieldType)}.
+ */
+ public StandardFieldMember(MemberFactory factory, JField field) {
+ super(field.getSourceInfo());
+ this.enclosing = factory.get(field.getEnclosingType());
+ this.sourceName = field.getEnclosingType().getName() + "::"
+ + field.getName();
+ }
+
+ public ClassMember getEnclosing() {
+ return enclosing;
+ }
+
+ @Override
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return "FieldMember " + sourceName;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardFunctionMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardFunctionMember.java
new file mode 100644
index 0000000..9b530b7
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardFunctionMember.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.FunctionMember;
+import com.google.gwt.dev.js.ast.JsFunction;
+
+/**
+ * An implementation of FunctionMember.
+ */
+public class StandardFunctionMember extends AbstractMemberWithDependencies
+ implements FunctionMember {
+
+ private final String sourceName;
+
+ /**
+ * Constructed by {@link MemberFactory#get(JsFunction)}.
+ */
+ public StandardFunctionMember(MemberFactory factory, JsFunction function) {
+ super(function.getSourceInfo());
+ this.sourceName = function.getName().getIdent();
+ }
+
+ @Override
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return "FunctionMember " + sourceName;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java
new file mode 100644
index 0000000..e617acf
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StandardMethodMember.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.core.ext.soyc.MethodMember;
+import com.google.gwt.dev.jjs.Correlation;
+import com.google.gwt.dev.jjs.Correlation.Axis;
+import com.google.gwt.dev.jjs.ast.JMethod;
+import com.google.gwt.dev.jjs.ast.JType;
+
+import java.util.Collections;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * An implementation of MethodMember.
+ */
+public class StandardMethodMember extends AbstractMemberWithDependencies
+ implements MethodMember {
+ private final SortedSet<String> aliasesView;
+ private final ClassMember enclosing;
+ private final String sourceName;
+ private final SortedSet<MethodMember> overridesView;
+
+ /**
+ * Constructed by {@link MemberFactory#get(JMethod)}.
+ */
+ public StandardMethodMember(MemberFactory factory, JMethod method) {
+ super(method.getSourceInfo());
+ this.enclosing = factory.get(method.getEnclosingType());
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(method.getEnclosingType().getName()).append("::");
+ sb.append(method.getName()).append("(");
+ for (JType type : method.getOriginalParamTypes()) {
+ sb.append(type.getJsniSignatureName());
+ }
+ sb.append(")");
+ this.sourceName = sb.toString();
+
+ SortedSet<String> aliases = new TreeSet<String>();
+ for (Correlation c : method.getSourceInfo().getAllCorrelations(
+ Axis.JS_ALIAS)) {
+ aliases.add(c.getName().getShortIdent());
+ }
+ aliasesView = Collections.unmodifiableSortedSet(aliases);
+
+ SortedSet<MethodMember> overrides = new TreeSet<MethodMember>(
+ Member.SOURCE_NAME_COMPARATOR);
+ for (JMethod override : method.overrides) {
+ overrides.add(factory.get(override));
+ }
+ overridesView = Collections.unmodifiableSortedSet(overrides);
+ }
+
+ public ClassMember getEnclosing() {
+ return enclosing;
+ }
+
+ public SortedSet<String> getJsAliases() {
+ return aliasesView;
+ }
+
+ public SortedSet<MethodMember> getOverrides() {
+ return overridesView;
+ }
+
+ @Override
+ public String getSourceName() {
+ return sourceName;
+ }
+
+ /**
+ * For debugging use only.
+ */
+ @Override
+ public String toString() {
+ return "MethodMember " + sourceName;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java
new file mode 100644
index 0000000..7aab089
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImpl.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.core.ext.soyc.Story;
+import com.google.gwt.dev.jjs.SourceInfo.Mutation;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * An implementation of the Story interface. This type has two additional pieces
+ * of information not required by the Story interface. The first is a unique id
+ * number and the second is a length. Instead of storing range objects for each
+ * StoryImpl, we simply store the StoryImpls in order and calculate the Range
+ * for the StoryImpl based on its length.
+ *
+ * @see SnippetIterator#next()
+ */
+public class StoryImpl implements Story, Serializable {
+ /**
+ * Orders StoryImpl's by their id number.
+ */
+ public static final Comparator<Story> ID_COMPARATOR = new StoryImplComparator();
+
+ private final int id;
+ private final int fragment;
+ private final int length;
+ private final String literalDescription;
+ private final SortedSet<Member> members;
+ private final List<String> mutations;
+ private final SortedSet<Origin> origins;
+
+ /**
+ * Standard constructor. This constructor will create unmodifiable versions of
+ * the collections passed into it.
+ */
+ public StoryImpl(int id, SortedSet<Member> members, List<Mutation> mutations,
+ SortedSet<Origin> origins, String literalDescription, int fragment,
+ int length) {
+ assert members != null;
+ assert mutations != null;
+ assert origins != null;
+ assert fragment >= 0;
+ assert length > 0;
+ // literalDescription may be null
+
+ this.id = id;
+ this.fragment = fragment;
+ this.length = length;
+ this.literalDescription = literalDescription == null ? null
+ : literalDescription.intern();
+ this.members = Collections.unmodifiableSortedSet(members);
+
+ List<String> mutableMutations = new ArrayList<String>(mutations.size());
+ for (Mutation m : mutations) {
+ mutableMutations.add(m.getDescription() + " by " + m.getCaller());
+ }
+ this.mutations = Collections.unmodifiableList(mutableMutations);
+ this.origins = Collections.unmodifiableSortedSet(origins);
+ }
+
+ /**
+ * This is a copy-constructor that's used when we subdivide a Range. All we
+ * really care about in the shadow version is having a different length; all
+ * of the other fields are initialized from the original.
+ */
+ public StoryImpl(StoryImpl other, int length) {
+ this.id = other.id;
+ this.fragment = other.fragment;
+ this.length = length;
+ this.literalDescription = other.literalDescription;
+ this.members = other.members;
+ this.mutations = other.mutations;
+ this.origins = other.origins;
+ }
+
+ /**
+ * Identity is based on the <code>id</code> field.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof StoryImpl)) {
+ return false;
+ }
+ StoryImpl o = (StoryImpl) obj;
+ return id == o.id;
+ }
+
+ public int getFragment() {
+ return fragment;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ /**
+ * Used internally, and not specified by the Story interface.
+ */
+ public int getLength() {
+ return length;
+ }
+
+ public String getLiteralTypeName() {
+ return literalDescription;
+ }
+
+ public SortedSet<Member> getMembers() {
+ return members;
+ }
+
+ public List<String> getMutations() {
+ return mutations;
+ }
+
+ public SortedSet<Origin> getSourceOrigin() {
+ return origins;
+ }
+
+ /**
+ * Identity is based on the <code>id</code> field.
+ */
+ @Override
+ public int hashCode() {
+ return id;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImplComparator.java b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImplComparator.java
new file mode 100644
index 0000000..f3daae8
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/impl/StoryImplComparator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.ext.soyc.impl;
+
+import com.google.gwt.core.ext.soyc.Story;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * Exists as a real class to allow TreeMaps to be serialized.
+ */
+public class StoryImplComparator implements Comparator<Story>, Serializable {
+ public int compare(Story o1, Story o2) {
+ return ((StoryImpl) o1).getId() - ((StoryImpl) o2).getId();
+ }
+
+ /**
+ * Use the singleton instance.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ return StoryImpl.ID_COMPARATOR;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/ext/soyc/package-info.java b/dev/core/src/com/google/gwt/core/ext/soyc/package-info.java
new file mode 100644
index 0000000..5cb0045
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/ext/soyc/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/**
+ * This package contains interfaces that provide access to
+ * "Story of Your Compile" information. When the compiler is run with analysis
+ * turned on, these types are available to Linkers via
+ * {@link com.google.gwt.core.ext.linker.CompilationAnalysis} artifacts.
+ */
+package com.google.gwt.core.ext.soyc;
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/core/linker/soyc/SoycReportLinker.java b/dev/core/src/com/google/gwt/core/linker/soyc/SoycReportLinker.java
new file mode 100644
index 0000000..4f74865
--- /dev/null
+++ b/dev/core/src/com/google/gwt/core/linker/soyc/SoycReportLinker.java
@@ -0,0 +1,863 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.linker.soyc;
+
+import com.google.gwt.core.ext.LinkerContext;
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.AbstractLinker;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.CompilationAnalysis;
+import com.google.gwt.core.ext.linker.CompilationResult;
+import com.google.gwt.core.ext.linker.LinkerOrder;
+import com.google.gwt.core.ext.linker.SelectionProperty;
+import com.google.gwt.core.ext.linker.SyntheticArtifact;
+import com.google.gwt.core.ext.linker.CompilationAnalysis.Snippet;
+import com.google.gwt.core.ext.linker.LinkerOrder.Order;
+import com.google.gwt.core.ext.soyc.ClassMember;
+import com.google.gwt.core.ext.soyc.FieldMember;
+import com.google.gwt.core.ext.soyc.FunctionMember;
+import com.google.gwt.core.ext.soyc.Member;
+import com.google.gwt.core.ext.soyc.MethodMember;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.core.ext.soyc.Story;
+import com.google.gwt.core.ext.soyc.Story.Origin;
+import com.google.gwt.dev.util.HtmlTextOutput;
+import com.google.gwt.util.tools.Utility;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Generates the XML report containing the Story of Your Compile.
+ */
+@LinkerOrder(Order.PRE)
+public class SoycReportLinker extends AbstractLinker {
+
+ int curNumIndents = 0;
+
+ public String escapeXml(String unescaped) {
+ String escaped = unescaped.replaceAll("\\&", "&");
+ escaped = escaped.replaceAll("\\<", "<");
+ escaped = escaped.replaceAll("\\>", ">");
+ escaped = escaped.replaceAll("\\\"", """);
+ // escaped = escaped.replaceAll("\\'", "'");
+ return escaped;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Story of your compile report";
+ }
+
+ @Override
+ public ArtifactSet link(TreeLogger logger, LinkerContext context,
+ ArtifactSet artifacts) throws UnableToCompleteException {
+ SortedSet<CompilationAnalysis> reports = artifacts.find(CompilationAnalysis.class);
+
+ // Do nothing if there are no reports to be generated.
+ if (reports.isEmpty()) {
+ return artifacts;
+ }
+
+ logger = logger.branch(TreeLogger.DEBUG, "SOYC report linker");
+ initialize(logger);
+
+ if (reports.isEmpty()) {
+ logger.log(TreeLogger.DEBUG, "No SOYC report artifacts");
+ return artifacts;
+ }
+
+ artifacts = new ArtifactSet(artifacts);
+ int reportNum = 0;
+ SortedMap<CompilationResult, String> partialPathsByResult = new TreeMap<CompilationResult, String>();
+
+ // TODO: This goes much faster in parallel, but what's the policy?
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ List<Future<SyntheticArtifact>> futures = new ArrayList<Future<SyntheticArtifact>>(
+ reports.size());
+ for (final CompilationAnalysis report : reports) {
+ final TreeLogger loopLogger = logger.branch(TreeLogger.SPAM,
+ "Report for " + report.toString());
+ final String reportName = "report" + reportNum++ + ".xml.gz";
+ partialPathsByResult.put(report.getCompilationResult(), reportName);
+ Future<SyntheticArtifact> future = executor.submit(new Callable<SyntheticArtifact>() {
+ public SyntheticArtifact call() throws Exception {
+ loopLogger.log(TreeLogger.INFO, "Started");
+ SyntheticArtifact reportArtifact = emitReport(loopLogger, report,
+ reportName, true);
+ return reportArtifact;
+ }
+ });
+ futures.add(future);
+ }
+ executor.shutdown();
+
+ for (Future<SyntheticArtifact> future : futures) {
+ SyntheticArtifact artifact;
+ try {
+ artifact = future.get();
+ } catch (InterruptedException e) {
+ logger.log(TreeLogger.ERROR, "Unable to process report", e);
+ throw new UnableToCompleteException();
+ } catch (ExecutionException e) {
+ logger.log(TreeLogger.ERROR, "Unable to process report", e);
+ throw new UnableToCompleteException();
+ }
+ artifact.setPrivate(true);
+ artifacts.add(artifact);
+ }
+
+ // Emit manifest
+ try {
+ SyntheticArtifact sa = emitManifest(logger, artifacts,
+ partialPathsByResult, false);
+ sa.setPrivate(true);
+ artifacts.add(sa);
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return artifacts;
+ }
+
+ private void emitAliases(HtmlTextOutput htmlOut, Set<String> methodAliases) {
+ String curLine;
+ if (methodAliases.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<aliases>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+
+ for (String methodAlias : methodAliases) {
+ curLine = "<alias jsName=\"" + methodAlias + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (methodAliases.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</aliases>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitClasses(HtmlTextOutput htmlOut,
+ SortedMap<String, Set<ClassMember>> packageToClasses, String packageName) {
+ String curLine;
+ /**
+ * sort the classes alphabetically
+ */
+ TreeMap<String, ClassMember> sortedClasses = new TreeMap<String, ClassMember>();
+ for (ClassMember classMember : packageToClasses.get(packageName)) {
+ String className = classMember.getSourceName();
+ sortedClasses.put(className, classMember);
+ }
+
+ for (String className : sortedClasses.keySet()) {
+ ClassMember classMember = sortedClasses.get(className);
+ curLine = "<class id=\"" + className + "\" ";
+ htmlOut.printRawOpt(curLine);
+
+ String jsName = classMember.getJsName();
+ String name = className.substring(className.lastIndexOf('.') + 1);
+ curLine = "jsName=\"" + jsName + "\" name=\"" + name + "\">";
+
+ if (jsName == null) {
+ curLine = "name=\"" + name + "\">";
+ }
+
+ emitOverrides(htmlOut, curLine, classMember);
+ emitDependencies(htmlOut, classMember);
+ emitMethods(htmlOut, classMember);
+ emitFields(htmlOut, classMember);
+
+ curLine = "</class>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ }
+
+ private void emitDependencies(HtmlTextOutput htmlOut, ClassMember classMember) {
+ String curLine;
+ Set<Member> dependencies = classMember.getDependencies();
+ if (dependencies.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<depends>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (Member dependency : dependencies) {
+ curLine = "<on idref=\"" + dependency.getSourceName() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (dependencies.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</depends>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitFields(HtmlTextOutput htmlOut, ClassMember classMember) {
+ String curLine;
+ Set<FieldMember> fields = classMember.getFields();
+ if (fields.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (FieldMember field : fields) {
+ curLine = "<field id=\"" + field.getSourceName() + "\" jsName=\""
+ + field.getJsName() + "\"/>";
+ String curJsName = field.getJsName();
+ if (curJsName == null) {
+ curLine = "<field id=\"" + field.getSourceName() + "\"/>";
+ }
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (fields.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitFunctions(CompilationAnalysis report, HtmlTextOutput htmlOut) {
+ String curLine;
+
+ Set<FunctionMember> functions = report.getFunctions();
+ for (FunctionMember function : functions) {
+ curLine = "<function ";
+ htmlOut.printRawOpt(curLine);
+
+ String sourceName = function.getSourceName();
+ String jsName = function.getJsName();
+ Set<Member> dependencies = function.getDependencies();
+ if (dependencies.size() == 0) {
+ curLine = "id=\"" + sourceName + "\" jsName=\"" + jsName + "\"/>";
+ if (jsName == null) {
+ curLine = "id=\"" + sourceName + "\"/>";
+ }
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ } else {
+ curLine = "id=\"" + sourceName + "\" jsName=\"" + jsName + "\">";
+ if (jsName == null) {
+ curLine = "id=\"" + sourceName + "\">";
+ }
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<depends>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (Member dependency : dependencies) {
+ curLine = "<on idref=\"" + dependency.getSourceName() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ }
+ if (dependencies.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ curLine = "</depends>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</function>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ }
+ }
+
+ private void emitJs(CompilationAnalysis report, HtmlTextOutput htmlOut,
+ Map<Story, Integer> storyIds) {
+
+ String curLine;
+ int fragment = 0;
+ for (String contents : report.getCompilationResult().getJavaScript()) {
+ curLine = "<js fragment=\"" + fragment + "\">";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ for (Snippet snippet : report.getSnippets(fragment)) {
+ Range range = snippet.getRange();
+ Story story = snippet.getStory();
+ assert storyIds.containsKey(story);
+ int storyId = storyIds.get(story);
+
+ String jsCode = contents.substring(range.getStart(), range.getEnd());
+ jsCode = escapeXml(jsCode);
+ if ((jsCode.length() == 0) || (jsCode.compareTo("\n") == 0)) {
+ curLine = "<storyref idref=\"story" + Integer.toString(storyId)
+ + "\"/>";
+ } else {
+ curLine = "<storyref idref=\"story" + Integer.toString(storyId)
+ + "\">" + jsCode + "</storyref>";
+ }
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</js>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ fragment++;
+ }
+ }
+
+ private SyntheticArtifact emitManifest(TreeLogger logger,
+ ArtifactSet artifacts,
+ SortedMap<CompilationResult, String> partialPathsByResult,
+ boolean compress) throws UnableToCompleteException, IOException {
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStreamWriter out;
+ try {
+ out = new OutputStreamWriter(compress ? new GZIPOutputStream(bytes)
+ : bytes);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to set up gzip stream", e);
+ throw new UnableToCompleteException();
+ }
+ PrintWriter pw = new PrintWriter(out);
+ HtmlTextOutput htmlOut = new HtmlTextOutput(pw, false);
+
+ String curLine = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ curLine = "<soyc-manifest>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ for (Map.Entry<CompilationResult, String> entry : partialPathsByResult.entrySet()) {
+ curLine = "<report href=\"" + entry.getValue() + "\">";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ for (Map<SelectionProperty, String> map : entry.getKey().getPropertyMap()) {
+
+ if (map.size() > 0) {
+ curLine = "<permutation>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ } else {
+ curLine = "<permutation/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ }
+ for (Map.Entry<SelectionProperty, String> propertyEntry : map.entrySet()) {
+ curLine = "<property name=\"" + propertyEntry.getKey().getName()
+ + "\" value=\"" + propertyEntry.getValue() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ }
+ if (map.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</permutation>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ }
+ }
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</report>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+ }
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</soyc-manifest>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newline();
+
+ pw.close();
+ Utility.close(out);
+ SyntheticArtifact toReturn = emitBytes(logger, bytes.toByteArray(),
+ "manifest.xml");
+
+ return toReturn;
+ }
+
+ private void emitMembers(CompilationAnalysis report, HtmlTextOutput htmlOut) {
+ String curLine;
+
+ curLine = "<members>";
+
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ SortedMap<String, Set<ClassMember>> packageToClasses = new TreeMap<String, Set<ClassMember>>();
+
+ emitPackages(report, htmlOut, packageToClasses);
+ emitFunctions(report, htmlOut);
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</members>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+
+ private void emitMethodDependencies(HtmlTextOutput htmlOut,
+ Set<Member> methodDependencies) {
+ String curLine;
+ if (methodDependencies.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<depends>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ for (Member methodDependency : methodDependencies) {
+ curLine = "<on idref=\"" + methodDependency.getSourceName() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</depends>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitMethodOverrides(HtmlTextOutput htmlOut,
+ Set<MethodMember> methodOverrides) {
+ String curLine;
+ if (methodOverrides.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<override>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (MethodMember overrideMethodMember : methodOverrides) {
+ curLine = "<of idref=\"" + overrideMethodMember.getSourceName() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (methodOverrides.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</override>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitMethods(HtmlTextOutput htmlOut, ClassMember classMember) {
+ String curLine;
+ Set<MethodMember> methods = classMember.getMethods();
+ if (methods.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (MethodMember method : methods) {
+ curLine = "<method ";
+ htmlOut.printRawOpt(curLine);
+
+ String jsAtt = " jsName=\"" + method.getJsName() + "\"";
+ String curJsName = method.getJsName();
+
+ if (curJsName == null) {
+ jsAtt = "";
+ }
+
+ Set<String> methodAliases = method.getJsAliases();
+ Set<MethodMember> methodOverrides = method.getOverrides();
+ Set<Member> methodDependencies = method.getDependencies();
+
+ if ((methodOverrides.size() > 0) || (methodDependencies.size() > 0)
+ || (methodAliases.size() > 0)) {
+ curLine = "id=\"" + method.getSourceName() + "\"" + jsAtt + ">";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ } else {
+ curLine = "id=\"" + method.getSourceName() + "\"" + jsAtt + "/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+
+ emitAliases(htmlOut, methodAliases);
+ emitMethodOverrides(htmlOut, methodOverrides);
+ emitMethodDependencies(htmlOut, methodDependencies);
+
+ if ((methodOverrides.size() > 0) || (methodDependencies.size() > 0)
+ || (methodAliases.size() > 0)) {
+ curLine = "</method>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ }
+
+ if (methods.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitOverrides(HtmlTextOutput htmlOut, String curLine,
+ ClassMember classMember) {
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ Set<ClassMember> overrides = classMember.getOverrides();
+ if (overrides.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ curLine = "<override>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (ClassMember overrideClassMember : overrides) {
+ curLine = "<of idref=\"" + overrideClassMember.getSourceName() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (overrides.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</override>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ }
+
+ private void emitPackages(CompilationAnalysis report, HtmlTextOutput htmlOut,
+ SortedMap<String, Set<ClassMember>> packageToClasses) {
+
+ String curLine;
+ for (ClassMember classMember : report.getClasses()) {
+ String packageName = classMember.getPackage();
+ if (packageToClasses.containsKey(packageName)) {
+ packageToClasses.get(packageName).add(classMember);
+ } else {
+ Set<ClassMember> insertSet = new HashSet<ClassMember>();
+ insertSet.add(classMember);
+ packageToClasses.put(packageName, insertSet);
+ }
+ }
+
+ for (String packageName : packageToClasses.keySet()) {
+
+ curLine = "<package id=\"" + packageName + "\">";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ if (packageToClasses.get(packageName).size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ emitClasses(htmlOut, packageToClasses, packageName);
+ if (packageToClasses.get(packageName).size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+
+ curLine = "</package>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ }
+
+ private SyntheticArtifact emitReport(TreeLogger logger,
+ CompilationAnalysis report, String partialPath, boolean compress)
+ throws UnableToCompleteException, IOException {
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ OutputStreamWriter out;
+ try {
+ out = new OutputStreamWriter(compress ? new GZIPOutputStream(bytes)
+ : bytes);
+ } catch (IOException e) {
+ logger.log(TreeLogger.ERROR, "Unable to set up gzip stream", e);
+ throw new UnableToCompleteException();
+ }
+ PrintWriter pw = new PrintWriter(out);
+ HtmlTextOutput htmlOut = new HtmlTextOutput(pw, false);
+
+ String curLine = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ curLine = "<soyc>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ emitMembers(report, htmlOut);
+ Map<Story, Integer> storyIds = emitStories(report, htmlOut);
+ emitJs(report, htmlOut, storyIds);
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</soyc>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ pw.close();
+ Utility.close(out);
+ SyntheticArtifact toReturn = emitBytes(logger, bytes.toByteArray(),
+ partialPath);
+
+ return toReturn;
+ }
+
+ private Map<Story, Integer> emitStories(CompilationAnalysis report,
+ HtmlTextOutput htmlOut) {
+
+ String curLine;
+ Map<Story, Integer> storyIds = new HashMap<Story, Integer>();
+ Set<Story> stories = report.getStories();
+ curLine = "<stories>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ if (stories.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (Story story : stories) {
+
+ int storyNum = storyIds.size();
+ storyIds.put(story, storyNum);
+
+ curLine = "<story id=\"story" + Integer.toString(storyNum) + "\"";
+ if (story.getLiteralTypeName() != null) {
+ curLine = curLine + " literal=\"" + story.getLiteralTypeName() + "\"";
+ }
+ curLine = curLine + ">";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ Set<Origin> origins = story.getSourceOrigin();
+ if (origins.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<origins>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (Origin origin : origins) {
+ curLine = "<origin lineNumber=\""
+ + Integer.toString(origin.getLineNumber()) + "\" location=\""
+ + origin.getLocation() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (origins.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</origins>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+
+ Set<Member> correlations = story.getMembers();
+ if (correlations.size() > 0) {
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+
+ curLine = "<correlations>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ htmlOut.indentIn();
+ htmlOut.indentIn();
+ curNumIndents++;
+ }
+ for (Member correlation : correlations) {
+ curLine = "<by idref=\"" + correlation.getSourceName() + "\"/>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (correlations.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+
+ curLine = "</correlations>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+
+ curLine = "</story>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+ }
+ if (stories.size() > 0) {
+ htmlOut.indentOut();
+ htmlOut.indentOut();
+ curNumIndents--;
+ }
+ curLine = "</stories>";
+ htmlOut.printRawOpt(curLine);
+ htmlOut.newlineOpt();
+
+ return storyIds;
+ }
+
+ private void initialize(TreeLogger logger) throws UnableToCompleteException {
+ logger = logger.branch(TreeLogger.SPAM, "Initializing");
+ }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index ec0e924..106c2e1 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -20,11 +20,14 @@
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.UnifiedAst;
+import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers;
import com.google.gwt.util.tools.ArgHandlerString;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -176,13 +179,8 @@
public static PermutationResult compile(TreeLogger logger,
Permutation permutation, UnifiedAst unifiedAst) {
try {
- final String js[] = JavaToJavaScriptCompiler.compilePermutation(logger,
- unifiedAst, permutation.getRebindAnswers());
- return new PermutationResult() {
- public String[] getJs() {
- return js;
- }
- };
+ return JavaToJavaScriptCompiler.compilePermutation(logger, unifiedAst,
+ permutation.getRebindAnswers());
} catch (UnableToCompleteException e) {
// We intentionally don't pass in the exception here since the real
// cause has been logged.
@@ -195,7 +193,8 @@
*/
public static boolean compile(TreeLogger logger,
Precompilation precompilation, Permutation[] perms, int localWorkers,
- File[] resultFiles) throws UnableToCompleteException {
+ List<FileBackedObject<PermutationResult>> resultFiles)
+ throws UnableToCompleteException {
final TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling "
+ perms.length + " permutations");
PermutationWorkerFactory.compilePermutations(logger, precompilation, perms,
@@ -227,12 +226,16 @@
System.exit(1);
}
- public static File[] makeResultFiles(File compilerWorkDir, Permutation[] perms) {
- File[] resultFiles = new File[perms.length];
+ public static List<FileBackedObject<PermutationResult>> makeResultFiles(
+ File compilerWorkDir, Permutation[] perms) {
+ List<FileBackedObject<PermutationResult>> toReturn = new ArrayList<FileBackedObject<PermutationResult>>(
+ perms.length);
for (int i = 0; i < perms.length; ++i) {
- resultFiles[i] = makePermFilename(compilerWorkDir, perms[i].getId());
+ File f = makePermFilename(compilerWorkDir, perms[i].getId());
+ toReturn.add(new FileBackedObject<PermutationResult>(
+ PermutationResult.class, f));
}
- return resultFiles;
+ return toReturn;
}
/**
@@ -291,7 +294,8 @@
}
}
- File[] resultFiles = makeResultFiles(options.getCompilerWorkDir(), perms);
+ List<FileBackedObject<PermutationResult>> resultFiles = makeResultFiles(
+ options.getCompilerWorkDir(), perms);
return compile(logger, precompilation, subPerms, options.getLocalWorkers(),
resultFiles);
}
diff --git a/dev/core/src/com/google/gwt/dev/CompilePermsServer.java b/dev/core/src/com/google/gwt/dev/CompilePermsServer.java
index ace3385..df14e98 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePermsServer.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePermsServer.java
@@ -16,8 +16,10 @@
package com.google.gwt.dev;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.jjs.UnifiedAst;
+import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.arg.ArgHandlerLogLevel;
import com.google.gwt.dev.util.arg.OptionLogLevel;
import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
@@ -289,20 +291,27 @@
} catch (ClassNotFoundException e) {
logger.log(TreeLogger.ERROR, "Probable client/server mismatch or "
+ "classpath misconfiguration", e);
+ } catch (UnableToCompleteException e) {
+ logger.log(TreeLogger.ERROR, "Internal compiler error", e);
}
return false;
}
static void compilePermutation(TreeLogger logger, UnifiedAst ast,
ObjectInputStream in, ObjectOutputStream out)
- throws ClassNotFoundException, IOException {
+ throws ClassNotFoundException, IOException, UnableToCompleteException {
+ FileBackedObject<PermutationResult> resultFile = (FileBackedObject<PermutationResult>) in.readObject();
Permutation p = (Permutation) in.readObject();
logger.log(TreeLogger.SPAM, "Permutation read");
PermutationResult result = CompilePerms.compile(logger.branch(
TreeLogger.DEBUG, "Compiling"), p, ast);
logger.log(TreeLogger.DEBUG, "Successfully compiled permutation");
- out.writeObject(result);
+
+ resultFile.set(logger, result);
+
+ // Send a placeholder null indicating no Throwable
+ out.writeObject(null);
out.flush();
logger.log(TreeLogger.SPAM, "Sent result");
}
diff --git a/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java b/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java
index 137144d..312e4bd 100644
--- a/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java
+++ b/dev/core/src/com/google/gwt/dev/ExternalPermutationWorkerFactory.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.UnifiedAst;
+import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.Util;
import java.io.BufferedReader;
@@ -99,7 +100,8 @@
this.serverSocket = sock;
}
- public PermutationResult compile(TreeLogger logger, Permutation permutation)
+ public void compile(TreeLogger logger, Permutation permutation,
+ FileBackedObject<PermutationResult> resultFile)
throws TransientWorkerException, UnableToCompleteException {
// If we've just started, we need to get a connection from a subprocess
@@ -139,9 +141,16 @@
try {
out.writeBoolean(true);
+ out.writeObject(resultFile);
out.writeObject(permutation);
out.flush();
- return (PermutationResult) in.readObject();
+
+ Throwable t = (Throwable) in.readObject();
+ if (t != null) {
+ logger.log(TreeLogger.ERROR, "Error from external worker", t);
+ throw new UnableToCompleteException();
+ }
+
} catch (IOException e) {
logger.log(TreeLogger.WARN, "Lost communication with remote process", e);
throw new TransientWorkerException(
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 546e5ab..4a74a45 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -22,16 +22,19 @@
import com.google.gwt.dev.Precompile.PrecompileOptionsImpl;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
import com.google.gwt.dev.util.arg.ArgHandlerLocalWorkers;
import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
+import com.google.gwt.dev.util.arg.ArgHandlerSoyc;
import com.google.gwt.dev.util.arg.ArgHandlerWorkDirOptional;
import com.google.gwt.util.tools.Utility;
import java.io.File;
import java.io.IOException;
+import java.util.List;
/**
* The main executable entry point for the GWT Java to JavaScript compiler.
@@ -48,6 +51,7 @@
registerHandler(new ArgHandlerExtraDir(options));
registerHandler(new ArgHandlerLocalWorkers(options));
registerHandler(new ArgHandlerOutDir(options));
+ registerHandler(new ArgHandlerSoyc(options));
}
@Override
@@ -153,7 +157,7 @@
module, options.getGenDir(), options.getCompilerWorkDir());
Permutation[] allPerms = precompilation.getPermutations();
- File[] resultFiles = CompilePerms.makeResultFiles(
+ List<FileBackedObject<PermutationResult>> resultFiles = CompilePerms.makeResultFiles(
options.getCompilerWorkDir(), allPerms);
CompilePerms.compile(logger, precompilation, allPerms,
options.getLocalWorkers(), resultFiles);
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java
index f10d910..99d2252 100644
--- a/dev/core/src/com/google/gwt/dev/Link.java
+++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -26,6 +26,7 @@
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.StaticPropertyOracle;
+import com.google.gwt.dev.util.FileBackedObject;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerExtraDir;
import com.google.gwt.dev.util.arg.ArgHandlerOutDir;
@@ -33,7 +34,9 @@
import com.google.gwt.dev.util.arg.OptionOutDir;
import java.io.File;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -100,7 +103,8 @@
}
public static ArtifactSet link(TreeLogger logger, ModuleDef module,
- Precompilation precompilation, File[] resultFiles)
+ Precompilation precompilation,
+ List<FileBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
module, precompilation.getUnifiedAst().getOptions());
@@ -108,7 +112,8 @@
}
public static void link(TreeLogger logger, ModuleDef module,
- Precompilation precompilation, File[] resultFiles, File outDir,
+ Precompilation precompilation,
+ List<FileBackedObject<PermutationResult>> resultFiles, File outDir,
File extrasDir) throws UnableToCompleteException {
StandardLinkerContext linkerContext = new StandardLinkerContext(logger,
module, precompilation.getUnifiedAst().getOptions());
@@ -142,15 +147,16 @@
private static ArtifactSet doLink(TreeLogger logger,
StandardLinkerContext linkerContext, Precompilation precompilation,
- File[] resultFiles) throws UnableToCompleteException {
+ List<FileBackedObject<PermutationResult>> resultFiles)
+ throws UnableToCompleteException {
Permutation[] perms = precompilation.getPermutations();
- if (perms.length != resultFiles.length) {
+ if (perms.length != resultFiles.size()) {
throw new IllegalArgumentException(
"Mismatched resultFiles.length and permutation count");
}
for (int i = 0; i < perms.length; ++i) {
- finishPermuation(logger, perms[i], resultFiles[i], linkerContext);
+ finishPermuation(logger, perms[i], resultFiles.get(i), linkerContext);
}
linkerContext.addOrReplaceArtifacts(precompilation.getGeneratedArtifacts());
@@ -200,10 +206,10 @@
}
private static void finishPermuation(TreeLogger logger, Permutation perm,
- File jsFile, StandardLinkerContext linkerContext)
- throws UnableToCompleteException {
+ FileBackedObject<PermutationResult> resultFile,
+ StandardLinkerContext linkerContext) throws UnableToCompleteException {
StandardCompilationResult compilation = linkerContext.getCompilation(
- logger, jsFile);
+ logger, resultFile);
StaticPropertyOracle[] propOracles = perm.getPropertyOracles();
for (StaticPropertyOracle propOracle : propOracles) {
BindingProperty[] orderedProps = propOracle.getOrderedProps();
@@ -254,16 +260,20 @@
return false;
}
Permutation[] perms = precompilation.getPermutations();
- File[] resultFiles = new File[perms.length];
+
+ List<FileBackedObject<PermutationResult>> resultFiles = new ArrayList<FileBackedObject<PermutationResult>>(
+ perms.length);
for (int i = 0; i < perms.length; ++i) {
- resultFiles[i] = CompilePerms.makePermFilename(
+ File permFile = CompilePerms.makePermFilename(
options.getCompilerWorkDir(), i);
- if (!resultFiles[i].exists()) {
+ if (!permFile.exists()) {
logger.log(TreeLogger.ERROR, "File not found '"
+ precompilationFile.getAbsolutePath()
+ "'; please compile all permutations");
return false;
}
+ resultFiles.add(new FileBackedObject<PermutationResult>(
+ PermutationResult.class, permFile));
}
TreeLogger branch = logger.branch(TreeLogger.INFO, "Linking module "
diff --git a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
deleted file mode 100644
index 5ae54b4..0000000
--- a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * Copyright 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.dev.cfg.BindingProperty;
-import com.google.gwt.dev.cfg.StaticPropertyOracle;
-import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
-import com.google.gwt.dev.jjs.UnifiedAst;
-import com.google.gwt.dev.util.PerfLogger;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Compiles a set of permutations, possibly in parallel in multiple threads.
- */
-public class PermutationCompiler {
-
- /**
- * Hands back results as they are finished.
- */
- public interface ResultsHandler {
- void addResult(Permutation permutation, int permNum, String[] js)
- throws UnableToCompleteException;
- }
-
- /**
- * A Result for a permutation that failed to compile.
- */
- private static final class FailedResult extends Result {
- private Throwable exception;
-
- public FailedResult(Permutation perm, int permNum, Throwable exception) {
- super(perm, permNum);
- this.exception = exception;
- }
-
- public Throwable getException() {
- return exception;
- }
- }
-
- /**
- * Represents the task of compiling a single permutation.
- */
- private static final class PermutationTask implements Callable<String[]> {
- private static void logProperties(TreeLogger logger,
- StaticPropertyOracle[] propOracles) {
- for (StaticPropertyOracle propOracle : propOracles) {
- BindingProperty[] props = propOracle.getOrderedProps();
- String[] values = propOracle.getOrderedPropValues();
- if (logger.isLoggable(TreeLogger.DEBUG)) {
- logger = logger.branch(TreeLogger.DEBUG, "Setting properties", null);
- for (int i = 0; i < props.length; i++) {
- String name = props[i].getName();
- String value = values[i];
- logger.log(TreeLogger.TRACE, name + " = " + value, null);
- }
- }
- }
- }
-
- private final UnifiedAst unifiedAst;
- private final TreeLogger logger;
- private final Permutation perm;
- private final int permNum;
-
- public PermutationTask(TreeLogger logger, UnifiedAst unifiedAst,
- Permutation perm, int permNum) {
- this.logger = logger;
- this.unifiedAst = unifiedAst;
- this.perm = perm;
- this.permNum = permNum;
- }
-
- public String[] call() throws Exception {
- PerfLogger.start("Permutation #" + permNum);
- try {
- TreeLogger branch = logger.branch(TreeLogger.TRACE, "Permutation #"
- + permNum);
- logProperties(branch, perm.getPropertyOracles());
- return JavaToJavaScriptCompiler.compilePermutation(branch, unifiedAst,
- perm.getRebindAnswers());
- } finally {
- PerfLogger.end();
- }
- }
-
- public int getPermNum() {
- return permNum;
- }
-
- public Permutation getPermutation() {
- return perm;
- }
- }
-
- /**
- * Contains the results of an attempt to compile.
- */
- private abstract static class Result {
- private final Permutation perm;
- private final int permNum;
-
- public Result(Permutation perm, int permNum) {
- this.perm = perm;
- this.permNum = permNum;
- }
-
- public int getPermNum() {
- return permNum;
- }
-
- public Permutation getPermutation() {
- return perm;
- }
- }
-
- /**
- * A Result for a permutation that succeeded.
- */
- private static final class SuccessResult extends Result {
- private final String[] js;
-
- public SuccessResult(Permutation perm, int permNum, String[] result) {
- super(perm, permNum);
- this.js = result;
- }
-
- public String[] getJs() {
- return js;
- }
- }
-
- /**
- * Implements a memory-sensitive worker thread to compile permutations.
- */
- private class WorkerThread implements Runnable {
- private PermutationTask currentTask;
-
- private final Runnable outOfMemoryRetryAction = new Runnable() {
- public void run() {
- currentTask.logger.log(
- TreeLogger.WARN,
- "Not enough memory to run another concurrent permutation, reducing thread count; "
- + "increasing the amount of memory by using the -Xmx flag "
- + "at startup may result in faster compiles");
- tasks.add(currentTask);
- }
- };
-
- public void run() {
- try {
- while (true) {
- doTask();
- }
- } catch (ThreadDeath expected) {
- }
- }
-
- protected void doTask() throws ThreadDeath {
- currentTask = tasks.poll();
- if (currentTask == null) {
- // Nothing left to do.
- tryToExitNonFinalThread(null);
-
- // As the last thread, I must inform the main thread we're all done.
- exitFinalThread(new Runnable() {
- public void run() {
- results.add(FINISHED_RESULT);
- }
- });
- }
-
- boolean definitelyFinalThread = (threadCount.get() == 1);
- try {
- String[] result = currentTask.call();
- results.add(new SuccessResult(currentTask.getPermutation(),
- currentTask.getPermNum(), result));
- } catch (OutOfMemoryError e) {
- if (definitelyFinalThread) {
- // OOM on the final thread, this is a truly unrecoverable failure.
- currentTask.logger.log(TreeLogger.ERROR, "Out of memory", e);
- exitFinalThread(new Runnable() {
- public void run() {
- results.add(new FailedResult(currentTask.getPermutation(),
- currentTask.getPermNum(), new UnableToCompleteException()));
- }
- });
- }
-
- /*
- * Try the task again with fewer threads, it may not OOM this time.
- */
- tryToExitNonFinalThread(outOfMemoryRetryAction);
-
- /*
- * Okay, so we actually are the final thread. However, we weren't the
- * final thread at the beginning of the compilation, so it's possible
- * that a retry may now succeed with only one active thread. Let's
- * optimistically retry one last time, and if this doesn't work, it's a
- * hard failure.
- */
- outOfMemoryRetryAction.run();
- } catch (Throwable e) {
- // Unexpected error compiling, this is unrecoverable.
- results.add(new FailedResult(currentTask.getPermutation(),
- currentTask.getPermNum(), e));
- throw new ThreadDeath();
- }
- }
-
- private void exitFinalThread(Runnable actionOnExit) {
- boolean isFinalThread = threadCount.compareAndSet(1, 0);
- assert isFinalThread;
- if (actionOnExit != null) {
- actionOnExit.run();
- }
- throw new ThreadDeath();
- }
-
- /**
- * Exits this thread if and only if it's not the last running thread,
- * performing the specified action before terminating.
- *
- * @param actionOnExit
- */
- private void tryToExitNonFinalThread(Runnable actionOnExit) {
- int remainingThreads = threadCount.decrementAndGet();
- if (remainingThreads == 0) {
- // We are definitely the last thread.
- threadCount.incrementAndGet();
- return;
- }
-
- // We are definitely not the last thread, and have removed our count.
- if (actionOnExit != null) {
- actionOnExit.run();
- }
- throw new ThreadDeath();
- }
- }
-
- /**
- * A marker Result that tells the main thread all work is done.
- */
- private static final Result FINISHED_RESULT = new Result(null, -1) {
- };
-
- /**
- * A queue of results being sent from worker threads to the main thread.
- */
- protected final BlockingQueue<Result> results = new LinkedBlockingQueue<Result>();
-
- /**
- * A queue of tasks being sent to the worker threads.
- */
- protected final ConcurrentLinkedQueue<PermutationTask> tasks = new ConcurrentLinkedQueue<PermutationTask>();
-
- /**
- * Tracks the number of live worker threads.
- */
- protected final AtomicInteger threadCount = new AtomicInteger();
-
- private final TreeLogger logger;
-
- public PermutationCompiler(TreeLogger logger, UnifiedAst unifiedAst,
- Permutation[] perms, int[] permsToRun) {
- this.logger = logger;
- for (int permToRun : permsToRun) {
- tasks.add(new PermutationTask(logger, unifiedAst, perms[permToRun],
- permToRun));
- }
- }
-
- public void go(ResultsHandler handler) throws UnableToCompleteException {
- int initialThreadCount = computeInitialThreadCount();
- Thread[] workerThreads = new Thread[initialThreadCount];
- for (int i = 0; i < initialThreadCount; ++i) {
- workerThreads[i] = new Thread(new WorkerThread());
- }
- threadCount.set(initialThreadCount);
- for (Thread thread : workerThreads) {
- thread.start();
- }
- try {
- while (true) {
- Result result = results.take();
- if (result == FINISHED_RESULT) {
- assert threadCount.get() == 0;
- return;
- } else if (result instanceof SuccessResult) {
- String[] js = ((SuccessResult) result).getJs();
- handler.addResult(result.getPermutation(), result.getPermNum(), js);
- } else if (result instanceof FailedResult) {
- FailedResult failedResult = (FailedResult) result;
- throw logAndTranslateException(failedResult.getException());
- }
- // Allow GC.
- result = null;
- }
-
- } catch (InterruptedException e) {
- throw new RuntimeException("Unexpected interruption", e);
- } finally {
- for (Thread thread : workerThreads) {
- if (thread.isAlive()) {
- thread.interrupt();
- }
- }
- }
- }
-
- private int computeInitialThreadCount() {
- /*
- * Don't need more threads than the number of permutations.
- */
- int result = tasks.size();
-
- /*
- * Computation is mostly CPU bound, so don't use more threads than
- * processors.
- */
- result = Math.min(Runtime.getRuntime().availableProcessors(), result);
-
- /*
- * User-defined value caps.
- */
- result = Math.min(result, Integer.getInteger("gwt.jjs.maxThreads", 1));
-
- return result;
- }
-
- private UnableToCompleteException logAndTranslateException(Throwable e) {
- if (e instanceof UnableToCompleteException) {
- return (UnableToCompleteException) e;
- } else {
- logger.log(TreeLogger.ERROR, "Unexpected compiler failure", e);
- return new UnableToCompleteException();
- }
- }
-}
diff --git a/dev/core/src/com/google/gwt/dev/PermutationResult.java b/dev/core/src/com/google/gwt/dev/PermutationResult.java
index 0b6f1bf..41fe2b0 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationResult.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationResult.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.dev;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+
import java.io.Serializable;
/**
@@ -22,6 +24,12 @@
*/
public interface PermutationResult extends Serializable {
/**
+ * Returns any Artifacts that may have been created as a result of compiling
+ * the permutation.
+ */
+ ArtifactSet getArtifacts();
+
+ /**
* The compiled JavaScript code.
*/
String[] getJs();
diff --git a/dev/core/src/com/google/gwt/dev/PermutationWorker.java b/dev/core/src/com/google/gwt/dev/PermutationWorker.java
index f24d35f..be16ba2 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationWorker.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationWorker.java
@@ -17,6 +17,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.dev.util.FileBackedObject;
/**
* Represents a facility that can compile an individual {@link Permutation}.
@@ -28,13 +29,15 @@
/**
* Compile a single permutation. The {@link com.google.gwt.dev.jjs.UnifiedAst}
* will have been provided to {@link PermutationWorkerFactory#getWorkers}
- * method.
+ * method. The compiled PermutationResult will be returned via the
+ * <code>resultFile</code> parameter.
*
* @throws TransientWorkerException if the Permutation should be tried again
* on another worker
* @throws UnableToCompleteException due to a fatal error
*/
- PermutationResult compile(TreeLogger logger, Permutation permutation)
+ void compile(TreeLogger logger, Permutation permutation,
+ FileBackedObject<PermutationResult> resultFile)
throws TransientWorkerException, UnableToCompleteException;
/**
diff --git a/dev/core/src/com/google/gwt/dev/PermutationWorkerFactory.java b/dev/core/src/com/google/gwt/dev/PermutationWorkerFactory.java
index 0322072..6c50d96 100644
--- a/dev/core/src/com/google/gwt/dev/PermutationWorkerFactory.java
+++ b/dev/core/src/com/google/gwt/dev/PermutationWorkerFactory.java
@@ -18,9 +18,8 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.UnifiedAst;
-import com.google.gwt.dev.util.Util;
+import com.google.gwt.dev.util.FileBackedObject;
-import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -43,8 +42,6 @@
*/
private static class Manager {
- private static final Work POISON_PILL = new Work(null, null, null);
-
private static enum Result {
SUCCESS, FAIL, WORKER_DEATH
}
@@ -69,8 +66,7 @@
}
TreeLogger logger = work.getLogger();
try {
- PermutationResult result = worker.compile(logger, work.getPerm());
- Util.writeObjectAsFile(logger, work.getResultFile(), result);
+ worker.compile(logger, work.getPerm(), work.getResultFile());
logger.log(TreeLogger.DEBUG, "Successfully compiled permutation");
resultsQueue.put(Result.SUCCESS);
} catch (TransientWorkerException e) {
@@ -98,6 +94,8 @@
}
}
+ private static final Work POISON_PILL = new Work(null, null, null);
+
public static void run(TreeLogger logger, List<Work> work,
List<PermutationWorker> workers) throws UnableToCompleteException {
new Manager().doRun(logger, work, workers);
@@ -179,9 +177,10 @@
private static class Work {
private final TreeLogger logger;
private final Permutation perm;
- private final File resultFile;
+ private final FileBackedObject<PermutationResult> resultFile;
- public Work(TreeLogger logger, Permutation perm, File resultFile) {
+ public Work(TreeLogger logger, Permutation perm,
+ FileBackedObject<PermutationResult> resultFile) {
this.logger = logger;
this.perm = perm;
this.resultFile = resultFile;
@@ -195,7 +194,7 @@
return perm;
}
- public File getResultFile() {
+ public FileBackedObject<PermutationResult> getResultFile() {
return resultFile;
}
}
@@ -220,7 +219,8 @@
* PermutationWorkersFactories.
*/
public static void compilePermutations(TreeLogger logger,
- Precompilation precompilation, int localWorkers, File[] resultFiles)
+ Precompilation precompilation, int localWorkers,
+ List<FileBackedObject<PermutationResult>> resultFiles)
throws UnableToCompleteException {
compilePermutations(logger, precompilation,
precompilation.getPermutations(), localWorkers, resultFiles);
@@ -241,8 +241,9 @@
*/
public static void compilePermutations(TreeLogger logger,
Precompilation precompilation, Permutation[] permutations,
- int localWorkers, File[] resultFiles) throws UnableToCompleteException {
- assert permutations.length == resultFiles.length;
+ int localWorkers, List<FileBackedObject<PermutationResult>> resultFiles)
+ throws UnableToCompleteException {
+ assert permutations.length == resultFiles.size();
assert Arrays.asList(precompilation.getPermutations()).containsAll(
Arrays.asList(permutations));
@@ -252,7 +253,7 @@
Permutation perm = permutations[i];
TreeLogger permLogger = logger.branch(TreeLogger.DEBUG,
"Worker permutation " + perm.getId() + " of " + permutations.length);
- work.add(new Work(permLogger, perm, resultFiles[i]));
+ work.add(new Work(permLogger, perm, resultFiles.get(i)));
}
// Create the workers.
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index c5a4d82..c6a1c97 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -122,6 +122,10 @@
return jjsOptions.isEnableAssertions();
}
+ public boolean isSoycEnabled() {
+ return jjsOptions.isSoycEnabled();
+ }
+
public boolean isValidateOnly() {
return validateOnly;
}
@@ -142,6 +146,10 @@
jjsOptions.setOutput(output);
}
+ public void setSoycEnabled(boolean enabled) {
+ jjsOptions.setSoycEnabled(enabled);
+ }
+
public void setValidateOnly(boolean validateOnly) {
this.validateOnly = validateOnly;
}
diff --git a/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java b/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
index 89eebf0..fe8e467 100644
--- a/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
+++ b/dev/core/src/com/google/gwt/dev/ThreadedPermutationWorkerFactory.java
@@ -18,6 +18,7 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.UnifiedAst;
+import com.google.gwt.dev.util.FileBackedObject;
import java.util.ArrayList;
import java.util.Collection;
@@ -40,11 +41,13 @@
this.id = id;
}
- public PermutationResult compile(final TreeLogger logger,
- final Permutation permutation) throws TransientWorkerException,
- UnableToCompleteException {
+ public void compile(TreeLogger logger, Permutation permutation,
+ FileBackedObject<PermutationResult> resultFile)
+ throws TransientWorkerException, UnableToCompleteException {
try {
- return CompilePerms.compile(logger, permutation, ast);
+ PermutationResult result = CompilePerms.compile(logger, permutation,
+ ast);
+ resultFile.set(logger, result);
} catch (OutOfMemoryError e) {
logger.log(TreeLogger.ERROR,
"OutOfMemoryError: Increase heap size or lower "
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
index f66bce6..5cc8bb9 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefLoader.java
@@ -241,6 +241,9 @@
ModuleDefSchema schema = new ModuleDefSchema(logger, this, moduleURL,
moduleDir, moduleDef);
ReflectiveParser.parse(logger, schema, r);
+ } catch (Throwable e) {
+ logger.log(TreeLogger.ERROR, "Unexpected error while processing XML", e);
+ throw new UnableToCompleteException();
} finally {
Utility.close(r);
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/Correlation.java b/dev/core/src/com/google/gwt/dev/jjs/Correlation.java
index f077d86..0688003 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/Correlation.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/Correlation.java
@@ -20,9 +20,15 @@
import com.google.gwt.dev.jjs.ast.JReferenceType;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.js.ast.JsFunction;
+import com.google.gwt.dev.js.ast.JsName;
+
+import org.apache.commons.collections.map.ReferenceMap;
import java.io.Serializable;
+import java.util.Collections;
import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.Map;
/**
* Each SourceInfo may define one or more axes by which it can be correlated
@@ -43,29 +49,17 @@
*/
public enum Axis {
/*
- * TODO(bobv): Consider whether or not this should be a proper class
- * hierarchy. The nice thing about an enum is that all possible types are
- * programmatically enumerable.
- *
- * Also, consider MODULE and PACKAGE values.
+ * Note to implementors: Possibly the switch statement in
+ * StandardCompilationArtifact if additional member-type enum values are
+ * added.
*/
/**
- * Represents a physical source file.
- */
- FILE(true, true),
-
- /**
* A Java class or interface type.
*/
CLASS(true, false),
/**
- * A Java method.
- */
- METHOD(true, false),
-
- /**
* A field defined within a Java type.
*/
FIELD(true, false),
@@ -73,7 +67,34 @@
/**
* A JavaScript function derived from a class or method.
*/
- FUNCTION(false, true);
+ FUNCTION(false, true),
+
+ /**
+ * Objects with global names may be aliased (e.g. polymorphic method
+ * dispatch).
+ */
+ JS_ALIAS(false, true),
+
+ /**
+ * The globally-unique identifier used to represent the Member in the
+ * compiled output.
+ */
+ JS_NAME(false, true),
+
+ /**
+ * Indicates a literal value in the original source.
+ */
+ LITERAL(true, true),
+
+ /**
+ * A Java method.
+ */
+ METHOD(true, false),
+
+ /**
+ * Represents a physical source file.
+ */
+ ORIGIN(true, true);
private final boolean isJava;
private final boolean isJs;
@@ -96,6 +117,49 @@
}
/**
+ * Specifies the type of literal value.
+ */
+ public enum Literal {
+ VOID("void"), NULL("null"), BYTE("byte"), SHORT("short"), INT("int"), LONG(
+ "long"), FLOAT("float"), DOUBLE("double"), BOOLEAN("boolean"), CHAR(
+ "char"), STRING("string"), CLASS("class"), JS_BOOLEAN("boolean", true), JS_NUMBER(
+ "number", true), JS_NULL("null", true), JS_STRING("string", true),
+ /**
+ * undefined isn't actually a literal in JS, but we more-or-less treat it as
+ * though it were.
+ */
+ JS_UNDEFINED("undefined", true);
+
+ private final String description;
+ private final boolean isJava;
+ private final boolean isJs;
+
+ private Literal(String description) {
+ this.description = description;
+ isJava = true;
+ isJs = false;
+ }
+
+ private Literal(String description, boolean isJs) {
+ this.description = description;
+ isJava = !isJs;
+ this.isJs = isJs;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public boolean isJava() {
+ return isJava;
+ }
+
+ public boolean isJs() {
+ return isJs;
+ }
+ }
+
+ /**
* Compares Correlations based on axis and idents. Note that due to inherent
* limitations of mapping AST nodes into Strings, this Comparator may not
* always agree with {@link Correlation#equals(Object)}.
@@ -111,12 +175,103 @@
}
};
+ /**
+ * This cuts down on the total number of Correlation objects allocated.
+ */
+ @SuppressWarnings("unchecked")
+ private static final Map<Object, Correlation> CANONICAL_MAP = Collections.synchronizedMap(new ReferenceMap(
+ ReferenceMap.WEAK, ReferenceMap.WEAK));
+
+ /**
+ * Correlations based on Literals are all the same, so we'll just cook up a
+ * Map to make {@link #by(Literal)} fast.
+ */
+ private static final Map<Literal, Correlation> LITERAL_CORRELATIONS = new EnumMap<Literal, Correlation>(
+ Literal.class);
+
+ static {
+ for (Literal l : Literal.values()) {
+ LITERAL_CORRELATIONS.put(l, new Correlation(Axis.LITERAL,
+ l.getDescription(), l));
+ }
+ }
+
public static Correlation by(JField field) {
- return new Correlation(Axis.FIELD, field.getEnclosingType().getName()
- + "::" + field.getName(), field);
+ Correlation toReturn = CANONICAL_MAP.get(field);
+ if (toReturn == null) {
+ toReturn = new Correlation(Axis.FIELD, field.getEnclosingType().getName()
+ + "::" + field.getName(), field);
+ CANONICAL_MAP.put(field, toReturn);
+ }
+ return toReturn;
}
public static Correlation by(JMethod method) {
+ Correlation toReturn = CANONICAL_MAP.get(method);
+ if (toReturn == null) {
+
+ toReturn = new Correlation(Axis.METHOD, getMethodIdent(method), method);
+ CANONICAL_MAP.put(method, toReturn);
+ }
+ return toReturn;
+ }
+
+ public static Correlation by(JReferenceType type) {
+ Correlation toReturn = CANONICAL_MAP.get(type);
+ if (toReturn == null) {
+ toReturn = new Correlation(Axis.CLASS, type.getName(), type);
+ CANONICAL_MAP.put(type, toReturn);
+ }
+ return toReturn;
+ }
+
+ public static Correlation by(JsFunction function) {
+ Correlation toReturn = CANONICAL_MAP.get(function);
+ if (toReturn == null) {
+ toReturn = new Correlation(Axis.FUNCTION, function.getName().getIdent(),
+ function);
+ CANONICAL_MAP.put(function, toReturn);
+ }
+ return toReturn;
+ }
+
+ /**
+ * Creates a JS_NAME Correlation.
+ */
+ public static Correlation by(JsName name) {
+ return by(name, false);
+ }
+
+ /**
+ * Creates either a JS_NAME or JS_ALIAS correlation, based on the value of
+ * <code>isAlias</code>.
+ */
+ public static Correlation by(JsName name, boolean isAlias) {
+ Correlation toReturn = CANONICAL_MAP.get(name);
+ if (toReturn == null) {
+ toReturn = new Correlation(isAlias ? Axis.JS_ALIAS : Axis.JS_NAME,
+ name.getIdent(), name);
+ CANONICAL_MAP.put(name, toReturn);
+ }
+ return toReturn;
+ }
+
+ public static Correlation by(Literal type) {
+ assert LITERAL_CORRELATIONS.containsKey(type);
+ return LITERAL_CORRELATIONS.get(type);
+ }
+
+ public static Correlation by(SourceOrigin origin) {
+ Correlation toReturn = CANONICAL_MAP.get(origin);
+ if (toReturn == null) {
+ toReturn = new Correlation(Axis.ORIGIN, origin.getFileName() + ":"
+ + origin.getStartLine(), origin);
+ CANONICAL_MAP.put(origin, toReturn);
+ }
+ return toReturn;
+ }
+
+ private static String getMethodIdent(JMethod method) {
StringBuilder sb = new StringBuilder();
sb.append(method.getEnclosingType().getName()).append("::");
sb.append(method.getName()).append("(");
@@ -124,30 +279,13 @@
sb.append(type.getJsniSignatureName());
}
sb.append(")");
-
- return new Correlation(Axis.METHOD, sb.toString(), method);
- }
-
- public static Correlation by(JReferenceType type) {
- return new Correlation(Axis.CLASS, type.getName(), type);
- }
-
- public static Correlation by(JsFunction function) {
- return new Correlation(Axis.FUNCTION, function.getName().getIdent(),
- function);
- }
-
- /**
- * Constructs a {@link Axis#FILE} Correlation.
- */
- public static Correlation by(String filename) {
- return new Correlation(Axis.FILE, filename, filename);
+ return sb.toString();
}
/**
* This may contain a reference to either a Java or Js AST node.
*/
- protected final Object astReference;
+ protected final Serializable astReference;
protected final Axis axis;
@@ -159,9 +297,13 @@
*/
protected final String ident;
- private Correlation(Axis axis, String ident, Object astReference) {
- if (ident == null) {
+ private Correlation(Axis axis, String ident, Serializable astReference) {
+ if (axis == null) {
+ throw new NullPointerException("axis");
+ } else if (ident == null) {
throw new NullPointerException("ident");
+ } else if (astReference == null) {
+ throw new NullPointerException("astReference");
}
this.axis = axis;
@@ -178,7 +320,7 @@
boolean astSame = astReference == c.astReference
|| (astReference != null && astReference.equals(c.astReference));
- return axis.equals(c.axis) && astSame;
+ return axis == c.axis && astSame;
}
public Axis getAxis() {
@@ -209,6 +351,14 @@
return ident;
}
+ public Literal getLiteral() {
+ if (axis == Axis.LITERAL) {
+ return (Literal) astReference;
+ } else {
+ return null;
+ }
+ }
+
public JMethod getMethod() {
if (axis == Axis.METHOD) {
return (JMethod) astReference;
@@ -217,6 +367,22 @@
}
}
+ public JsName getName() {
+ if (axis == Axis.JS_NAME || axis == Axis.JS_ALIAS) {
+ return (JsName) astReference;
+ } else {
+ return null;
+ }
+ }
+
+ public SourceOrigin getOrigin() {
+ if (axis == Axis.ORIGIN) {
+ return (SourceOrigin) astReference;
+ } else {
+ return null;
+ }
+ }
+
public JReferenceType getType() {
if (axis == Axis.CLASS) {
return (JReferenceType) astReference;
@@ -231,7 +397,12 @@
@Override
public int hashCode() {
- return 37 * axis.hashCode() + astReference.hashCode() + 13;
+ /*
+ * The null checks are because this method gets called during
+ * deserialization, but without values having been set.
+ */
+ return 37 * (axis == null ? 1 : axis.hashCode())
+ + (astReference == null ? 0 : astReference.hashCode()) + 13;
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
index 6e9e162..770e932 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -18,10 +18,11 @@
import com.google.gwt.dev.util.arg.OptionAggressivelyOptimize;
import com.google.gwt.dev.util.arg.OptionEnableAssertions;
import com.google.gwt.dev.util.arg.OptionScriptStyle;
+import com.google.gwt.dev.util.arg.OptionSoycEnabled;
/**
* Controls options for the {@link JavaToJavaScriptCompiler}.
*/
public interface JJSOptions extends OptionAggressivelyOptimize,
- OptionEnableAssertions, OptionScriptStyle {
+ OptionEnableAssertions, OptionScriptStyle, OptionSoycEnabled {
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
index 8873c41..5001319 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -25,6 +25,7 @@
private boolean aggressivelyOptimize = true;
private boolean enableAssertions;
private JsOutputOption output = JsOutputOption.OBFUSCATED;
+ private boolean soycEnabled = false;
public JJSOptionsImpl() {
}
@@ -37,6 +38,7 @@
setAggressivelyOptimize(other.isAggressivelyOptimize());
setEnableAssertions(other.isEnableAssertions());
setOutput(other.getOutput());
+ setSoycEnabled(other.isSoycEnabled());
}
public JsOutputOption getOutput() {
@@ -51,6 +53,10 @@
return enableAssertions;
}
+ public boolean isSoycEnabled() {
+ return soycEnabled;
+ }
+
public void setAggressivelyOptimize(boolean aggressivelyOptimize) {
this.aggressivelyOptimize = aggressivelyOptimize;
}
@@ -62,4 +68,8 @@
public void setOutput(JsOutputOption output) {
this.output = output;
}
+
+ public void setSoycEnabled(boolean enabled) {
+ soycEnabled = enabled;
+ }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 2d8d57a..4d818a6 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -17,6 +17,10 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.linker.ArtifactSet;
+import com.google.gwt.core.ext.linker.impl.StandardCompilationAnalysis;
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.dev.PermutationResult;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
import com.google.gwt.dev.jjs.InternalCompilerException.NodeInfo;
@@ -48,7 +52,6 @@
import com.google.gwt.dev.jjs.impl.FragmentLoaderCreator;
import com.google.gwt.dev.jjs.impl.GenerateJavaAST;
import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
-import com.google.gwt.dev.jjs.impl.JavaAndJavaScript;
import com.google.gwt.dev.jjs.impl.JavaScriptObjectNormalizer;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.jjs.impl.JsoDevirtualizer;
@@ -70,12 +73,14 @@
import com.google.gwt.dev.js.JsNormalizer;
import com.google.gwt.dev.js.JsObfuscateNamer;
import com.google.gwt.dev.js.JsPrettyNamer;
+import com.google.gwt.dev.js.JsReportGenerationVisitor;
import com.google.gwt.dev.js.JsSourceGenerationVisitor;
import com.google.gwt.dev.js.JsStaticEval;
import com.google.gwt.dev.js.JsStringInterner;
import com.google.gwt.dev.js.JsSymbolResolver;
import com.google.gwt.dev.js.JsUnusedFunctionRemover;
import com.google.gwt.dev.js.JsVerboseNamer;
+import com.google.gwt.dev.js.JsReportGenerationVisitor.CountingTextOutput;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsStatement;
@@ -97,11 +102,28 @@
import java.util.TreeSet;
/**
- * Compiles the Java <code>JProgram</code> representation into its
- * corresponding JavaScript source.
+ * Compiles the Java <code>JProgram</code> representation into its corresponding
+ * JavaScript source.
*/
public class JavaToJavaScriptCompiler {
+ private static class PermutationResultImpl implements PermutationResult {
+ private final ArtifactSet artifacts = new ArtifactSet();
+ private final String[] js;
+
+ public PermutationResultImpl(String[] js) {
+ this.js = js;
+ }
+
+ public ArtifactSet getArtifacts() {
+ return artifacts;
+ }
+
+ public String[] getJs() {
+ return js;
+ }
+ }
+
/**
* Compiles a particular permutation, based on a precompiled unified AST.
*
@@ -114,16 +136,9 @@
* @throws UnableToCompleteException if an error other than
* {@link OutOfMemoryError} occurs
*/
- public static String[] compilePermutation(TreeLogger logger,
+ public static PermutationResult compilePermutation(TreeLogger logger,
UnifiedAst unifiedAst, Map<String, String> rebindAnswers)
throws UnableToCompleteException {
- return compilePermutationToJavaAndJavaScript(logger, unifiedAst,
- rebindAnswers).jscode;
- }
-
- public static JavaAndJavaScript compilePermutationToJavaAndJavaScript(
- TreeLogger logger, UnifiedAst unifiedAst,
- Map<String, String> rebindAnswers) throws UnableToCompleteException {
try {
if (JProgram.isTracingEnabled()) {
System.out.println("------------------------------------------------------------");
@@ -222,17 +237,34 @@
// (12) Generate the final output text.
String[] js = new String[jsProgram.getFragmentCount()];
- for (int i = 0; i < js.length; i++) {
+ List<Map<Range, SourceInfo>> sourceInfoMaps = options.isSoycEnabled()
+ ? new ArrayList<Map<Range, SourceInfo>>(jsProgram.getFragmentCount())
+ : null;
- DefaultTextOutput out = new DefaultTextOutput(
- options.getOutput().shouldMinimize());
- JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
- v.accept(jsProgram.getFragmentBlock(i));
- js[i] = out.toString();
+ for (int i = 0; i < js.length; i++) {
+ if (sourceInfoMaps != null) {
+ CountingTextOutput out = new CountingTextOutput(
+ options.getOutput().shouldMinimize());
+ JsReportGenerationVisitor v = new JsReportGenerationVisitor(out);
+ v.accept(jsProgram.getFragmentBlock(i));
+ js[i] = out.toString();
+ sourceInfoMaps.add(v.getSourceInfoMap());
+ } else {
+ DefaultTextOutput out = new DefaultTextOutput(
+ options.getOutput().shouldMinimize());
+ JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out);
+ v.accept(jsProgram.getFragmentBlock(i));
+ js[i] = out.toString();
+ }
}
- return new JavaAndJavaScript(jprogram, jsProgram, js,
- postStringInterningMap);
+ PermutationResult toReturn = new PermutationResultImpl(js);
+ if (sourceInfoMaps != null) {
+ toReturn.getArtifacts().add(
+ new StandardCompilationAnalysis(logger, sourceInfoMaps));
+ }
+
+ return toReturn;
} catch (Throwable e) {
throw logAndTranslateException(logger, e);
}
@@ -283,8 +315,8 @@
checkForErrors(logger, goldenCuds, false);
PerfLogger.start("Build AST");
- JProgram jprogram = new JProgram();
- JsProgram jsProgram = new JsProgram();
+ JProgram jprogram = new JProgram(options.isSoycEnabled());
+ JsProgram jsProgram = new JsProgram(options.isSoycEnabled());
try {
/*
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
index c8d9e9b..f4381b8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceInfo.java
@@ -18,15 +18,12 @@
import com.google.gwt.dev.jjs.Correlation.Axis;
import java.io.Serializable;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.HashSet;
-import java.util.Map;
+import java.util.List;
import java.util.Set;
/**
@@ -35,6 +32,32 @@
public class SourceInfo implements Serializable {
/**
+ * Describes how the SourceInfo's node was mutated during the compile cycle.
+ */
+ public static final class Mutation implements Serializable {
+ private final String caller;
+ private final String description;
+ private final long ts = System.currentTimeMillis();
+
+ private Mutation(String description, String caller) {
+ this.caller = caller;
+ this.description = description;
+ }
+
+ public String getCaller() {
+ return caller;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public long getTimestamp() {
+ return ts;
+ }
+ }
+
+ /**
* A totally-immutable version of SourceInfo.
*/
protected static class Immutable extends SourceInfo {
@@ -44,95 +67,93 @@
}
@Override
- public void addAdditonalAncestors(SourceInfo... sourceInfos) {
- throw new UnsupportedOperationException(
- "May not add additional ancestors to the " + getFileName()
- + " SourceInfo");
- }
-
- @Override
public void addCorrelation(Correlation c) {
throw new UnsupportedOperationException(
- "May not add correlations to the " + getFileName() + "SourceInfo");
+ "May not add correlations to the " + getFileName()
+ + "SourceInfo. Call makeChild() first.");
}
@Override
- public void addSupertypeAncestors(SourceInfo... sourceInfos) {
+ public void copyMissingCorrelationsFrom(SourceInfo other) {
throw new UnsupportedOperationException(
- "May not add supertype ancestors to the " + getFileName()
- + " SourceInfo");
+ "May not copy correlations into the " + getFileName()
+ + "SourceInfo. Call makeChild() first.");
+ }
+
+ @Override
+ public void merge(SourceInfo... sourceInfos) {
+ if (sourceInfos.length > 0) {
+ throw new UnsupportedOperationException(
+ "May not merge SourceInfos into the " + getFileName()
+ + " SourceInfo. Call makeChild() first.");
+ }
}
}
/**
- * Compares SourceInfos by their file and position information.
- */
- public static final Comparator<SourceInfo> LOCATION_COMPARATOR = new Comparator<SourceInfo>() {
- public int compare(SourceInfo q, SourceInfo b) {
- int a = q.getFileName().compareTo(b.getFileName());
- if (a != 0) {
- return a;
- }
-
- a = q.startPos - q.startPos;
- if (a != 0) {
- return a;
- }
-
- a = q.endPos - b.endPos;
- if (a != 0) {
- return a;
- }
-
- a = q.startLine - b.startLine;
- if (a != 0) {
- return a;
- }
-
- return 0;
- }
- };
-
- /**
* Indicates that the source for an AST element is unknown. This indicates a
* deficiency in the compiler.
*/
public static final SourceInfo UNKNOWN = new Immutable(0, 0, 0,
"Unknown source", true);
- private static final SourceInfo[] EMPTY_SOURCEINFO_ARRAY = new SourceInfo[0];
- private final Set<SourceInfo> additionalAncestors = new HashSet<SourceInfo>();
- private final String caller;
- private final EnumMap<Axis, Correlation> correlations = new EnumMap<Axis, Correlation>(
- Axis.class);
/**
- * This flag controls the behavior of {@link #makeChild}.
+ * Collecting mutation data is expensive in terms of additional objects and
+ * string literals and only of interest to compiler hackers, so we'll just
+ * normally have it disabled.
*/
- private final boolean createDescendants;
- private final int endPos;
- private transient Reference<Set<SourceInfo>> lazyRoots;
- private final String mutation;
- private final SourceInfo parent;
- private final int startLine;
- private final int startPos;
+ private static final boolean COLLECT_MUTATIONS = Boolean.getBoolean("gwt.soyc.collectMutations");
- private final Set<SourceInfo> supertypeAncestors = new HashSet<SourceInfo>();
+ /**
+ * Micro-opt for {@link #makeChild(Class, String)}.
+ */
+ private static final SourceInfo[] EMPTY_SOURCEINFO_ARRAY = new SourceInfo[0];
+
+ /**
+ * This flag controls the behavior of the mutable methods to make them no-ops.
+ */
+ private final boolean accumulateData;
+
+ /**
+ * Any Correlation associated with the SourceInfo.
+ */
+ private final Set<Correlation> allCorrelations;
+
+ /**
+ * Holds Mutation objects if the compiler is configured to collect mutations.
+ */
+ private final List<Mutation> mutations = COLLECT_MUTATIONS
+ ? new ArrayList<Mutation>() : null;
+
+ /**
+ * Holds the origin data for the SourceInfo.
+ */
+ private final SourceOrigin origin;
+
+ /**
+ * Records the first Correlation on any given Axis applied to the SourceInfo.
+ */
+ private final EnumMap<Axis, Correlation> primaryCorrelations;
protected SourceInfo(int startPos, int endPos, int startLine,
- String fileName, boolean createDescendants) {
+ String fileName, boolean accumulateData) {
assert fileName != null;
- this.createDescendants = createDescendants;
- this.startPos = startPos;
- this.endPos = endPos;
- this.startLine = startLine;
- this.parent = null;
- this.mutation = null;
- this.caller = null;
+ this.accumulateData = accumulateData;
+ origin = SourceOrigin.create(startPos, endPos, startLine, fileName);
- // Don't use addCorrelation because of the immutable subclasses
- Correlation file = Correlation.by(fileName);
- correlations.put(file.getAxis(), file);
+ // Be very aggressive in not allocating collections that we don't need.
+ if (accumulateData) {
+ allCorrelations = new HashSet<Correlation>();
+ primaryCorrelations = new EnumMap<Axis, Correlation>(Axis.class);
+ // Don't use addCorrelation because of the immutable subclasses
+ Correlation originCorrelation = Correlation.by(origin);
+ allCorrelations.add(originCorrelation);
+ primaryCorrelations.put(Axis.ORIGIN, originCorrelation);
+ } else {
+ allCorrelations = null;
+ primaryCorrelations = null;
+ }
}
private SourceInfo(SourceInfo parent, String mutation, String caller,
@@ -141,69 +162,60 @@
assert mutation != null;
assert caller != null;
- this.createDescendants = parent.createDescendants;
- this.startPos = parent.startPos;
- this.endPos = parent.endPos;
- this.startLine = parent.startLine;
- this.additionalAncestors.addAll(Arrays.asList(additionalAncestors));
- this.additionalAncestors.addAll(parent.additionalAncestors);
- this.parent = parent;
- this.mutation = mutation;
- this.caller = caller;
- }
+ this.accumulateData = parent.accumulateData;
+ this.origin = parent.origin;
- /**
- * Add additional ancestor SourceInfos. These SourceInfo objects indicate that
- * a merge-type operation took place or that the additional ancestors have a
- * containment relationship with the SourceInfo.
- */
- public void addAdditonalAncestors(SourceInfo... sourceInfos) {
- if (!createDescendants) {
- return;
+ if (accumulateData) {
+ this.allCorrelations = new HashSet<Correlation>(parent.allCorrelations);
+ this.primaryCorrelations = new EnumMap<Axis, Correlation>(
+ parent.primaryCorrelations);
+ } else {
+ allCorrelations = null;
+ primaryCorrelations = null;
}
- additionalAncestors.addAll(Arrays.asList(sourceInfos));
- additionalAncestors.remove(this);
-
- if (lazyRoots != null) {
- lazyRoots.clear();
+ if (COLLECT_MUTATIONS) {
+ this.mutations.addAll(parent.mutations);
+ this.mutations.add(new Mutation(mutation, caller));
}
+
+ merge(additionalAncestors);
}
/**
* Add a Correlation to the SourceInfo.
- *
- * @throws IllegalArgumentException if a Correlation with the same Axis had
- * been previously added to the SourceInfo. The reason for this is
- * that a Correlation shouldn't be re-applied to the same SourceInfo
- * node, if this were done, the caller should have also called
- * makeChild() first, since something interesting is gong on.
*/
public void addCorrelation(Correlation c) {
- if (!createDescendants) {
+ if (!accumulateData) {
return;
}
- Axis axis = c.getAxis();
+ allCorrelations.add(c);
- if (correlations.containsKey(axis)) {
- throw new IllegalArgumentException("Correlation on axis " + axis
- + " has already been added. Call makeChild() first.");
+ if (!primaryCorrelations.containsKey(c.getAxis())) {
+ primaryCorrelations.put(c.getAxis(), c);
}
-
- correlations.put(axis, c);
}
/**
- * Add SourceInfos that indicate the supertype derivation.
+ * Copy any Correlations from another SourceInfo node if there are no
+ * Correlations on this SourceInfo with the same Axis.
*/
- public void addSupertypeAncestors(SourceInfo... sourceInfos) {
- if (!createDescendants) {
+ public void copyMissingCorrelationsFrom(SourceInfo other) {
+ if (!accumulateData) {
return;
}
- supertypeAncestors.addAll(Arrays.asList(sourceInfos));
- supertypeAncestors.remove(this);
+ EnumSet<Axis> toAdd = EnumSet.allOf(Axis.class);
+ for (Correlation c : allCorrelations) {
+ toAdd.remove(c.getAxis());
+ }
+
+ for (Correlation c : other.getAllCorrelations()) {
+ if (toAdd.contains(c.getAxis())) {
+ addCorrelation(c);
+ }
+ }
}
/**
@@ -211,16 +223,8 @@
* ancestor SourceInfo, and any supertype SourceInfos.
*/
public Set<Correlation> getAllCorrelations() {
- EnumMap<Axis, Set<Correlation>> accumulator = new EnumMap<Axis, Set<Correlation>>(
- Axis.class);
- findCorrelations(accumulator, EnumSet.allOf(Axis.class), false);
-
- Set<Correlation> toReturn = new HashSet<Correlation>();
- for (Set<Correlation> toAdd : accumulator.values()) {
- toReturn.addAll(toAdd);
- }
-
- return Collections.unmodifiableSet(toReturn);
+ return accumulateData ? allCorrelations
+ : Collections.<Correlation> emptySet();
}
/**
@@ -228,253 +232,123 @@
* parent, additional ancestor SourceInfo, and any supertype SourceInfos.
*/
public Set<Correlation> getAllCorrelations(Axis axis) {
- EnumMap<Axis, Set<Correlation>> accumulator = new EnumMap<Axis, Set<Correlation>>(
- Axis.class);
- findCorrelations(accumulator, EnumSet.of(axis), false);
- assert accumulator.size() < 2;
- if (accumulator.size() == 0) {
- return Collections.unmodifiableSet(new HashSet<Correlation>());
- } else {
- assert accumulator.containsKey(axis);
- assert accumulator.get(axis).size() > 0;
- return Collections.unmodifiableSet(accumulator.get(axis));
- }
- }
-
- public int getEndPos() {
- return endPos;
- }
-
- public String getFileName() {
- return getPrimaryCorrelation(Axis.FILE).getIdent();
- }
-
- /**
- * Return the most-derived Correlation along a given Axis or <code>null</code>
- * if no such correlation exists. The search path uses the current SourceInfo,
- * parent chain, and additional ancestors, but not supertype SourceInfos.
- */
- public Correlation getPrimaryCorrelation(Axis axis) {
- EnumMap<Axis, Set<Correlation>> accumulator = new EnumMap<Axis, Set<Correlation>>(
- Axis.class);
- findCorrelations(accumulator, EnumSet.of(axis), true);
- assert accumulator.size() < 2;
- if (accumulator.size() == 0) {
- return null;
- } else {
- assert accumulator.containsKey(axis);
- assert accumulator.get(axis).size() == 1;
- return accumulator.get(axis).iterator().next();
- }
- }
-
- /**
- * Returns the most-derived Correlations along each Axis on which a
- * Correlation has been set. The search path uses the current SourceInfo,
- * parent chain, and additional ancestors, but not supertype SourceInfos.
- */
- public Set<Correlation> getPrimaryCorrelations() {
- EnumMap<Axis, Set<Correlation>> accumulator = new EnumMap<Axis, Set<Correlation>>(
- Axis.class);
- findCorrelations(accumulator, EnumSet.allOf(Axis.class), true);
-
- EnumMap<Axis, Correlation> toReturn = new EnumMap<Axis, Correlation>(
- Axis.class);
- for (Map.Entry<Axis, Set<Correlation>> entry : accumulator.entrySet()) {
- assert entry.getValue().size() == 1;
- toReturn.put(entry.getKey(), entry.getValue().iterator().next());
+ if (!accumulateData) {
+ return Collections.emptySet();
}
- return Collections.unmodifiableSet(new HashSet<Correlation>(
- toReturn.values()));
- }
-
- /**
- * Returns the SourceInfos from which this SourceInfo was ultimately derived.
- * SourceInfo objects which were not derived from others, via
- * {@link #makeChild}, will list itself as its root SourceInfo.
- */
- public Set<SourceInfo> getRoots() {
- if (parent == null && additionalAncestors.isEmpty()) {
- // If parent is null, we shouldn't have additional ancestors
- return Collections.unmodifiableSet(new HashSet<SourceInfo>(
- Collections.singleton(this)));
-
- } else if (additionalAncestors.size() == 0) {
- // This is a fairly typical case, where a node only has a parent
- return parent.getRoots();
+ Set<Correlation> toReturn = new HashSet<Correlation>();
+ for (Correlation c : getAllCorrelations()) {
+ if (c.getAxis() == axis) {
+ toReturn.add(c);
+ }
}
-
- // See if previously-computed work is available
- Set<SourceInfo> roots;
- if (lazyRoots != null && (roots = lazyRoots.get()) != null) {
- return roots;
- }
-
- // Otherwise, do some actual work
- roots = new HashSet<SourceInfo>();
-
- if (parent == null) {
- roots.add(this);
- } else {
- roots.addAll(parent.getRoots());
- }
-
- for (SourceInfo ancestor : additionalAncestors) {
- roots.addAll(ancestor.getRoots());
- }
-
- Set<SourceInfo> toReturn = Collections.unmodifiableSet(roots);
- lazyRoots = new SoftReference<Set<SourceInfo>>(toReturn);
return toReturn;
}
+ public int getEndPos() {
+ return getOrigin().getEndPos();
+ }
+
+ public String getFileName() {
+ return getOrigin().getFileName();
+ }
+
+ /**
+ * Returns a summary of the mutations applied to the SourceInfo. It it
+ * expensive to collect mutation data, so this method will only return useful
+ * values if the <code>gwt.jjs.collectMutations</code> system property is
+ * defined.
+ */
+ public List<Mutation> getMutations() {
+ if (COLLECT_MUTATIONS) {
+ return mutations;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ public SourceOrigin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Returns the first Correlation that had been set with a given Axis, or
+ * <code>null</code> if no Correlation has been set on the given axis.
+ */
+ public Correlation getPrimaryCorrelation(Axis axis) {
+ return accumulateData ? primaryCorrelations.get(axis) : null;
+ }
+
+ /**
+ * Returns the first Correlations added along each Axis on which a Correlation
+ * has been set.
+ */
+ public Set<Correlation> getPrimaryCorrelations() {
+ return accumulateData ? new HashSet<Correlation>(
+ primaryCorrelations.values()) : Collections.<Correlation> emptySet();
+ }
+
public int getStartLine() {
- return startLine;
+ return getOrigin().getStartLine();
}
public int getStartPos() {
- return startPos;
- }
-
- public String getStory() {
- /*
- * TODO(bobv): This is a temporary implementation. At some point it should
- * return a proper object which tools could use.
- */
- StringBuilder toReturn = new StringBuilder();
-
- if (caller != null) {
- SourceInfo si = this;
- while (si != null) {
- if (si.mutation != null) {
- toReturn.append(si.mutation + " by " + si.caller + "\n ");
- }
- si = si.parent;
- }
- }
-
- Set<SourceInfo> roots = getRoots();
- if (!roots.isEmpty()) {
- toReturn.append("\nRoots:\n");
- for (SourceInfo root : roots) {
- toReturn.append(" " + root.getFileName() + ":" + root.getStartLine()
- + "\n");
- }
- }
-
- Set<Correlation> allCorrelations = getAllCorrelations();
- Set<Correlation> primaryCorrelations = getPrimaryCorrelations();
- if (!allCorrelations.isEmpty()) {
- toReturn.append("\nCorrelations:\n");
- for (Correlation c : allCorrelations) {
- toReturn.append((primaryCorrelations.contains(c) ? " *" : " ") + c
- + "\n");
- }
- }
-
- Set<SourceInfo> supertypes = supertypeAncestors;
- if (!supertypes.isEmpty()) {
- toReturn.append("\nSupertypes:\n{\n");
- for (SourceInfo info : supertypes) {
- toReturn.append(info.getStory());
- }
- toReturn.append("\n}\n");
- }
-
- return toReturn.toString();
+ return getOrigin().getStartPos();
}
/**
- * Returns <code>true</code> if {@link #getAllCorrelations()} would return a
- * Correlation that has one or more of the specifies Axes.
- */
- public boolean hasCorrelation(Set<Axis> axes) {
- // Try local information
- if (!correlations.isEmpty()) {
- for (Axis a : axes) {
- if (correlations.containsKey(a)) {
- return true;
- }
- }
- }
-
- // Try the parent chain
- if (parent != null && parent.hasCorrelation(axes)) {
- return true;
- }
-
- // Try additional ancestors
- for (SourceInfo info : additionalAncestors) {
- if (info.hasCorrelation(axes)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Create a derived SourceInfo object. If SourceInfo collection is disabled,
- * this method will return the current object.
+ * If data accumulation is enabled, create a derived SourceInfo object that
+ * indicates that one or more AST nodes were merged to create a new node. The
+ * derived node will inherit its Origin and Correlations from the SourceInfo
+ * object on which the method is invoked.
*/
public SourceInfo makeChild(Class<?> caller, String description) {
- return makeChild(caller, description, EMPTY_SOURCEINFO_ARRAY);
+ return accumulateData ? makeChild(caller, description,
+ EMPTY_SOURCEINFO_ARRAY) : this;
}
/**
- * Create a derived SourceInfo object that indicates that one or more AST
- * nodes were merged to create a new node. The derived node will inherit its
- * location from the SourceInfo object on which the method is invoked.
+ * If data accumulation is enabled, create a derived SourceInfo object that
+ * indicates that one or more AST nodes were merged to create a new node. The
+ * derived node will inherit its Origin and Correlations from the SourceInfo
+ * object on which the method is invoked.
*/
public SourceInfo makeChild(Class<?> caller, String description,
- SourceInfo... additionalAncestors) {
- if (!createDescendants) {
+ SourceInfo... merge) {
+ if (!accumulateData) {
return this;
}
String callerName = caller == null ? "Unrecorded caller" : caller.getName();
- return new SourceInfo(this, description, callerName, additionalAncestors);
+ return new SourceInfo(this, description, callerName, merge);
}
/**
- * Implementation of the various getCorrelations functions.
+ * Add additional ancestor SourceInfos. These SourceInfo objects indicate that
+ * a merge-type operation took place or that the additional ancestors have a
+ * containment relationship with the SourceInfo.
*/
- private void findCorrelations(EnumMap<Axis, Set<Correlation>> accumulator,
- EnumSet<Axis> filter, boolean derivedOnly) {
- // Short circuit if all possible values have been seen
- if (derivedOnly && accumulator.size() == filter.size()) {
+ public void merge(SourceInfo... sourceInfos) {
+ if (!accumulateData) {
return;
}
- for (Map.Entry<Axis, Correlation> entry : correlations.entrySet()) {
- Axis key = entry.getKey();
- boolean containsKey = accumulator.containsKey(key);
- Correlation value = entry.getValue();
-
- if (containsKey) {
- if (!derivedOnly) {
- accumulator.get(key).add(value);
- }
- } else if (filter.contains(key)) {
- Set<Correlation> set = new HashSet<Correlation>();
- set.add(value);
- accumulator.put(key, derivedOnly ? Collections.unmodifiableSet(set)
- : set);
+ for (SourceInfo info : sourceInfos) {
+ if (this == info || !info.accumulateData) {
+ continue;
}
- }
- if (parent != null) {
- parent.findCorrelations(accumulator, filter, derivedOnly);
- }
+ allCorrelations.addAll(info.getAllCorrelations());
- for (SourceInfo info : additionalAncestors) {
- info.findCorrelations(accumulator, filter, derivedOnly);
- }
+ if (primaryCorrelations.size() < Axis.values().length) {
+ EnumMap<Axis, Correlation> copy = new EnumMap<Axis, Correlation>(
+ info.primaryCorrelations);
+ copy.keySet().removeAll(primaryCorrelations.keySet());
+ primaryCorrelations.putAll(copy);
+ }
- if (!derivedOnly) {
- for (SourceInfo info : supertypeAncestors) {
- info.findCorrelations(accumulator, filter, derivedOnly);
+ if (COLLECT_MUTATIONS) {
+ mutations.addAll(0, info.getMutations());
}
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
new file mode 100644
index 0000000..0b57c9f
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/jjs/SourceOrigin.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.jjs;
+
+import org.apache.commons.collections.map.ReferenceMap;
+
+import java.io.Serializable;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Describes where a SourceInfo's node came from. This class currently includes
+ * only physical origin information, but could be extended to provide support
+ * for source-Module and -Generators.
+ */
+public final class SourceOrigin implements Serializable {
+ /**
+ * This is synchronized since several threads could operate on it at once
+ * during parallel optimization phases.
+ */
+ @SuppressWarnings("unchecked")
+ private static final Map<SourceOrigin, SourceOrigin> CANONICAL_SOURCE_ORIGINS = Collections.synchronizedMap(new ReferenceMap(
+ ReferenceMap.WEAK, ReferenceMap.SOFT));
+
+ /**
+ * Creates SourceOrigin nodes. This factory method will attempt to provide
+ * canonicalized instances of SourceOrigin objects.
+ */
+ public static SourceOrigin create(int startPos, int endPos, int startLine,
+ String fileName) {
+
+ SourceOrigin newInstance = new SourceOrigin(fileName, startLine, startPos,
+ endPos);
+ SourceOrigin canonical = CANONICAL_SOURCE_ORIGINS.get(newInstance);
+
+ assert canonical == null
+ || (newInstance != canonical && newInstance.equals(canonical));
+
+ if (canonical != null) {
+ return canonical;
+ } else {
+ CANONICAL_SOURCE_ORIGINS.put(newInstance, newInstance);
+ return newInstance;
+ }
+ }
+
+ // TODO: Add Module and Generator tracking
+ private final int endPos;
+ private final String fileName;
+ private final int hash;
+ private final int startLine;
+ private final int startPos;
+
+ private SourceOrigin(String location, int startLine, int startPos, int endPos) {
+ this.fileName = location;
+ this.startLine = startLine;
+ this.startPos = startPos;
+ this.endPos = endPos;
+
+ // Go ahead and compute the hash, since it'll be used for canonicalization
+ this.hash = 13 * endPos + 17 * fileName.hashCode() + 29 * startLine + 31
+ * startPos + 2;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SourceOrigin)) {
+ return false;
+ }
+ SourceOrigin other = (SourceOrigin) o;
+ return endPos == other.endPos && fileName.equals(other.fileName)
+ && startLine == other.startLine && startPos == other.startPos;
+ }
+
+ public int getEndPos() {
+ return endPos;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public int getStartLine() {
+ return startLine;
+ }
+
+ public int getStartPos() {
+ return startPos;
+ }
+
+ @Override
+ public int hashCode() {
+ return hash;
+ }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
index 0ef2689..081bc6a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -15,8 +15,10 @@
*/
package com.google.gwt.dev.jjs.ast;
+import com.google.gwt.dev.jjs.Correlation;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.Correlation.Literal;
import com.google.gwt.dev.jjs.ast.JField.Disposition;
import com.google.gwt.dev.jjs.ast.js.JClassSeed;
import com.google.gwt.dev.jjs.ast.js.JsniMethodBody;
@@ -138,6 +140,15 @@
return traceMethods.size() > 0;
}
+ /**
+ * This method is used to create SourceInfos for fields in JProgram. This
+ * method always creates a SourceInfo that has collection enabled.
+ */
+ private static SourceInfo createSourceInfoEnabled(String description) {
+ return new SourceInfoJava(-1, -1, 0, JProgram.class.getName(), true).makeChild(
+ JProgram.class, description);
+ }
+
private static String dotify(char[][] name) {
StringBuffer result = new StringBuffer();
for (int i = 0; i < name.length; ++i) {
@@ -181,7 +192,7 @@
*/
private final ArrayList<HashMap<JType, JArrayType>> dimensions = new ArrayList<HashMap<JType, JArrayType>>();
- private boolean enableSourceInfoDescendants;
+ private final boolean enableSourceInfoDescendants;
private final Map<String, JField> indexedFields = new HashMap<String, JField>();
@@ -194,25 +205,25 @@
private List<JsonObject> jsonTypeTable;
private final JAbsentArrayDimension literalAbsentArrayDim = new JAbsentArrayDimension(
- this, createSourceInfoSynthetic(JProgram.class, "Absent array dimension"));
+ this, createSourceInfoEnabled("Absent array dimension"));
private final JBooleanLiteral literalFalse = new JBooleanLiteral(this,
- createSourceInfoSynthetic(JProgram.class, "false literal"), false);
+ createSourceInfoEnabled("false literal"), false);
private final JIntLiteral literalIntNegOne = new JIntLiteral(this,
- createSourceInfoSynthetic(JProgram.class, "-1 literal"), -1);
+ createSourceInfoEnabled("-1 literal"), -1);
private final JIntLiteral literalIntOne = new JIntLiteral(this,
- createSourceInfoSynthetic(JProgram.class, "1 literal"), 1);
+ createSourceInfoEnabled("1 literal"), 1);
private final JIntLiteral literalIntZero = new JIntLiteral(this,
- createSourceInfoSynthetic(JProgram.class, "0 literal"), 0);
+ createSourceInfoEnabled("0 literal"), 0);
private final JNullLiteral literalNull = new JNullLiteral(this,
- createSourceInfoSynthetic(JProgram.class, "null literal"));
+ createSourceInfoEnabled("null literal"));
private final JBooleanLiteral literalTrue = new JBooleanLiteral(this,
- createSourceInfoSynthetic(JProgram.class, "true literal"), true);
+ createSourceInfoEnabled("true literal"), true);
private JField nullField;
@@ -259,7 +270,7 @@
private final Map<String, JReferenceType> typeNameMap = new HashMap<String, JReferenceType>();
private final JNullType typeNull = new JNullType(this,
- createSourceInfoSynthetic(JProgram.class, "null type"));
+ createSourceInfoEnabled("null type"));
private final JPrimitiveType typeShort = new JPrimitiveType(this, "short",
"S", "java.lang.Short", literalIntZero);
@@ -273,9 +284,36 @@
private final JPrimitiveType typeVoid = new JPrimitiveType(this, "void", "V",
"java.lang.Void", null);
+ private final SourceInfo stringPoolSourceInfo;
+
+ private final Map<String, JStringLiteral> stringLiteralMap = new HashMap<String, JStringLiteral>();
+
public JProgram() {
+ this(false);
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param enableSourceInfoDescendants Controls whether or not SourceInfo nodes
+ * created via the JProgram will record descendant information.
+ * Enabling this feature will collect extra data during the
+ * compilation cycle, but at a cost of memory and object allocations.
+ */
+ public JProgram(boolean enableSourceInfoDescendants) {
super(null, SourceInfoJava.INTRINSIC.makeChild(JProgram.class,
"Top-level program"));
+
+ this.enableSourceInfoDescendants = enableSourceInfoDescendants;
+ literalFalse.getSourceInfo().addCorrelation(Correlation.by(Literal.BOOLEAN));
+ literalIntNegOne.getSourceInfo().addCorrelation(Correlation.by(Literal.INT));
+ literalIntOne.getSourceInfo().addCorrelation(Correlation.by(Literal.INT));
+ literalIntZero.getSourceInfo().addCorrelation(Correlation.by(Literal.INT));
+ literalNull.getSourceInfo().addCorrelation(Correlation.by(Literal.NULL));
+ literalTrue.getSourceInfo().addCorrelation(Correlation.by(Literal.BOOLEAN));
+ stringPoolSourceInfo = createSourceInfoSynthetic(JProgram.class,
+ "String pool");
+ stringPoolSourceInfo.addCorrelation(Correlation.by(Literal.STRING));
}
public void addEntryMethod(JMethod entryPoint) {
@@ -581,8 +619,9 @@
public JCharLiteral getLiteralChar(char c) {
// could be interned
- return new JCharLiteral(this, createSourceInfoSynthetic(JProgram.class, c
- + " literal"), c);
+ SourceInfo info = createSourceInfoSynthetic(JProgram.class, c + " literal");
+ info.addCorrelation(Correlation.by(Literal.CHAR));
+ return new JCharLiteral(this, info, c);
}
/**
@@ -620,8 +659,10 @@
0).getBody();
clinitBody.getStatements().add(decl);
- classLiteral = new JClassLiteral(this, createSourceInfoSynthetic(
- JProgram.class, "class literal for " + type.getName()), type, field);
+ SourceInfo literalInfo = createSourceInfoSynthetic(JProgram.class,
+ "class literal for " + type.getName());
+ literalInfo.addCorrelation(Correlation.by(Literal.CLASS));
+ classLiteral = new JClassLiteral(this, literalInfo, type, field);
classLiterals.put(type, classLiteral);
} else {
// Make sure the field hasn't been pruned.
@@ -647,14 +688,16 @@
public JDoubleLiteral getLiteralDouble(double d) {
// could be interned
- return new JDoubleLiteral(this, createSourceInfoSynthetic(JProgram.class, d
- + " literal"), d);
+ SourceInfo info = createSourceInfoSynthetic(JProgram.class, d + " literal");
+ info.addCorrelation(Correlation.by(Literal.DOUBLE));
+ return new JDoubleLiteral(this, info, d);
}
public JFloatLiteral getLiteralFloat(float f) {
// could be interned
- return new JFloatLiteral(this, createSourceInfoSynthetic(JProgram.class, f
- + " literal"), f);
+ SourceInfo info = createSourceInfoSynthetic(JProgram.class, f + " literal");
+ info.addCorrelation(Correlation.by(Literal.FLOAT));
+ return new JFloatLiteral(this, info, f);
}
public JIntLiteral getLiteralInt(int i) {
@@ -665,16 +708,20 @@
return literalIntZero;
case 1:
return literalIntOne;
- default:
+ default: {
// could be interned
- return new JIntLiteral(this, createSourceInfoSynthetic(JProgram.class,
- i + " literal"), i);
+ SourceInfo info = createSourceInfoSynthetic(JProgram.class, i
+ + " literal");
+ info.addCorrelation(Correlation.by(Literal.INT));
+ return new JIntLiteral(this, info, i);
+ }
}
}
public JLongLiteral getLiteralLong(long l) {
- return new JLongLiteral(this, createSourceInfoSynthetic(JProgram.class, l
- + " literal"), l);
+ SourceInfo info = createSourceInfoSynthetic(JProgram.class, l + " literal");
+ info.addCorrelation(Correlation.by(Literal.LONG));
+ return new JLongLiteral(this, info, l);
}
public JNullLiteral getLiteralNull() {
@@ -682,13 +729,18 @@
}
public JStringLiteral getLiteralString(SourceInfo sourceInfo, char[] s) {
- // should consolidate so we can build a string table in output code later?
- return new JStringLiteral(this, sourceInfo, String.valueOf(s));
+ return getLiteralString(sourceInfo, String.valueOf(s));
}
public JStringLiteral getLiteralString(SourceInfo sourceInfo, String s) {
- // should consolidate so we can build a string table in output code later?
- return new JStringLiteral(this, sourceInfo, s);
+ JStringLiteral toReturn = stringLiteralMap.get(s);
+ if (toReturn == null) {
+ toReturn = new JStringLiteral(this, stringPoolSourceInfo.makeChild(
+ JProgram.class, "String literal: " + s), s);
+ stringLiteralMap.put(s, toReturn);
+ }
+ toReturn.getSourceInfo().merge(sourceInfo);
+ return toReturn;
}
public JField getNullField() {
@@ -863,16 +915,6 @@
}
/**
- * Controls whether or not SourceInfo nodes created via the JProgram will
- * record descendant information. Enabling this feature will collect extra
- * data during the compilation cycle, but at a cost of memory and object
- * allocations.
- */
- public void setEnableSourceInfoDescendants(boolean enable) {
- enableSourceInfoDescendants = enable;
- }
-
- /**
* If <code>method</code> is a static impl method, returns the instance
* method that <code>method</code> is the implementation of. Otherwise,
* returns <code>null</code>.
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
index 7771dfe..99f9121 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/BuildTypeMap.java
@@ -512,7 +512,7 @@
// The SourceInfo will inherit Correlations from its enclosing object
if (enclosing != null) {
- toReturn.addAdditonalAncestors(enclosing.getSourceInfo());
+ toReturn.copyMissingCorrelationsFrom(enclosing.getSourceInfo());
}
return toReturn;
@@ -526,7 +526,7 @@
// The SourceInfo will inherit Correlations from its enclosing object
if (enclosing != null) {
- toReturn.addAdditonalAncestors(enclosing.getSourceInfo());
+ toReturn.copyMissingCorrelationsFrom(enclosing.getSourceInfo());
}
return toReturn;
@@ -617,7 +617,6 @@
assert (binding.superclass().isClass() || binding.superclass().isEnum());
JClassType superClass = (JClassType) typeMap.get(superClassBinding);
type.extnds = superClass;
- type.getSourceInfo().addSupertypeAncestors(superClass.getSourceInfo());
}
ReferenceBinding[] superInterfaces = binding.superInterfaces();
@@ -626,8 +625,6 @@
assert (superInterfaceBinding.isInterface());
JInterfaceType superInterface = (JInterfaceType) typeMap.get(superInterfaceBinding);
type.implments.add(superInterface);
- type.getSourceInfo().addSupertypeAncestors(
- superInterface.getSourceInfo());
}
if (type instanceof JEnumType) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index d3cbdfb..0b896dd 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.dev.jjs.impl;
-import com.google.gwt.dev.jjs.Correlation;
import com.google.gwt.dev.jjs.HasSourceInfo;
import com.google.gwt.dev.jjs.InternalCompilerException;
import com.google.gwt.dev.jjs.SourceInfo;
@@ -2246,10 +2245,10 @@
SourceInfo toReturn = program.createSourceInfo(x.sourceStart,
x.sourceEnd, startLine, currentFileName);
if (currentClass != null) {
- toReturn.addCorrelation(Correlation.by(currentClass));
+ toReturn.copyMissingCorrelationsFrom(currentClass.getSourceInfo());
}
if (currentMethod != null) {
- toReturn.addCorrelation(Correlation.by(currentMethod));
+ toReturn.copyMissingCorrelationsFrom(currentMethod.getSourceInfo());
}
return toReturn;
}
@@ -2421,7 +2420,6 @@
if (!method.overrides.contains(upRef)) {
method.overrides.add(upRef);
- method.getSourceInfo().addSupertypeAncestors(upRef.getSourceInfo());
break;
}
}
@@ -2452,8 +2450,6 @@
JMethod upRef = (JMethod) typeMap.get(tryMethod);
if (!method.overrides.contains(upRef)) {
method.overrides.add(upRef);
- method.getSourceInfo().addSupertypeAncestors(
- upRef.getSourceInfo());
break;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
index a05cb76..44d322a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptAST.java
@@ -162,7 +162,9 @@
String name = x.getName();
String mangleName = mangleName(x);
if (x.isStatic()) {
- names.put(x, topScope.declareName(mangleName, name));
+ JsName jsName = topScope.declareName(mangleName, name);
+ x.getSourceInfo().addCorrelation(Correlation.by(jsName));
+ names.put(x, jsName);
} else {
JsName jsName;
if (x == arrayLengthField) {
@@ -174,6 +176,7 @@
} else {
jsName = peek().declareName(mangleName, name);
}
+ x.getSourceInfo().addCorrelation(Correlation.by(jsName));
names.put(x, jsName);
}
}
@@ -239,7 +242,9 @@
}
// My seed function name
- names.put(x, topScope.declareName(getNameString(x), x.getShortName()));
+ JsName jsName = topScope.declareName(getNameString(x), x.getShortName());
+ x.getSourceInfo().addCorrelation(Correlation.by(jsName));
+ names.put(x, jsName);
// My class scope
if (x.extnds == null) {
@@ -289,6 +294,8 @@
} else {
polyName = interfaceScope.declareName(mangleName, name);
}
+ // Record this as an alias, not the primary name
+ x.getSourceInfo().addCorrelation(Correlation.by(polyName, true));
polymorphicNames.put(x, polyName);
}
}
@@ -304,6 +311,7 @@
assert x.getEnclosingType() != null;
String mangleName = mangleNameForGlobal(x);
globalName = topScope.declareName(mangleName, name);
+ x.getSourceInfo().addCorrelation(Correlation.by(globalName));
names.put(x, globalName);
JsFunction jsFunction;
@@ -320,6 +328,7 @@
}
methodBodyMap.put(x.getBody(), jsFunction);
jsFunction.getSourceInfo().addCorrelation(Correlation.by(jsFunction));
+ jsFunction.getSourceInfo().addCorrelation(Correlation.by(globalName));
push(jsFunction.getScope());
return true;
}
@@ -1442,10 +1451,6 @@
JsNameRef superPrototypeRef = names.get(x.extnds).makeRef(sourceInfo);
newExpr.setConstructorExpression(superPrototypeRef);
JsNode<?> staticRef = superPrototypeRef.getName().getStaticRef();
- if (staticRef != null) {
- seedFunc.getSourceInfo().addSupertypeAncestors(
- staticRef.getSourceInfo());
- }
rhs = newExpr;
} else {
rhs = new JsObjectLiteral(sourceInfo);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
index f6152e8..0e4a148 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaScriptLiterals.java
@@ -58,30 +58,32 @@
@Override
public final void endVisit(JCharLiteral x, Context ctx) {
- push(program.getNumberLiteral(x.getValue()));
+ push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
}
@Override
public final void endVisit(JDoubleLiteral x, Context ctx) {
- push(program.getNumberLiteral(x.getValue()));
+ push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
}
@Override
public final void endVisit(JFloatLiteral x, Context ctx) {
- push(program.getNumberLiteral(x.getValue()));
+ push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
}
@Override
public final void endVisit(JIntLiteral x, Context ctx) {
- push(program.getNumberLiteral(x.getValue()));
+ push(program.getNumberLiteral(x.getSourceInfo(), x.getValue()));
}
@Override
public void endVisit(JLongLiteral x, Context ctx) {
JsArrayLiteral arrayLit = new JsArrayLiteral(x.getSourceInfo());
double[] doubleArray = LongLib.typeChange(x.getValue());
- arrayLit.getExpressions().add(program.getNumberLiteral(doubleArray[0]));
- arrayLit.getExpressions().add(program.getNumberLiteral(doubleArray[1]));
+ arrayLit.getExpressions().add(
+ program.getNumberLiteral(x.getSourceInfo(), doubleArray[0]));
+ arrayLit.getExpressions().add(
+ program.getNumberLiteral(x.getSourceInfo(), doubleArray[1]));
push(arrayLit);
}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsParser.java b/dev/core/src/com/google/gwt/dev/js/JsParser.java
index 32a5fed..750f102 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsParser.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsParser.java
@@ -157,7 +157,7 @@
SourceInfo parent = sourceInfoStack.peek();
SourceInfo toReturn = program.createSourceInfo(node.getLineno()
+ parent.getStartLine() + 1, parent.getFileName());
- toReturn.addAdditonalAncestors(parent);
+ toReturn.copyMissingCorrelationsFrom(parent);
return toReturn;
}
diff --git a/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
new file mode 100644
index 0000000..080a38e
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.js;
+
+import com.google.gwt.core.ext.soyc.Range;
+import com.google.gwt.dev.jjs.HasSourceInfo;
+import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.js.ast.JsVisitable;
+import com.google.gwt.dev.util.AbstractTextOutput;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A variation on the standard source generation visitor that records the
+ * locations of SourceInfo objects in the output.
+ */
+public class JsReportGenerationVisitor extends JsSourceGenerationVisitor {
+ /**
+ * A TextOutput that can return the total number of characters that have been
+ * printed.
+ */
+ public static class CountingTextOutput extends AbstractTextOutput {
+ private final StringWriter sw = new StringWriter();
+ private final PrintWriter out = new PrintWriter(sw);
+
+ public CountingTextOutput(boolean compact) {
+ super(compact);
+ setPrintWriter(out);
+ }
+
+ public int getCount() {
+ out.flush();
+ return sw.getBuffer().length();
+ }
+
+ @Override
+ public String toString() {
+ out.flush();
+ return sw.toString();
+ }
+ }
+
+ private final Map<Range, SourceInfo> sourceInfoMap = new HashMap<Range, SourceInfo>();
+ private final CountingTextOutput out;
+
+ public JsReportGenerationVisitor(CountingTextOutput out) {
+ super(out);
+ this.out = out;
+ }
+
+ public Map<Range, SourceInfo> getSourceInfoMap() {
+ return Collections.unmodifiableMap(sourceInfoMap);
+ }
+
+ @Override
+ protected <T extends JsVisitable<T>> T doAccept(T node) {
+ boolean addEntry = node instanceof HasSourceInfo;
+ int start = addEntry ? out.getCount() : 0;
+ T toReturn = super.doAccept(node);
+ if (addEntry) {
+ SourceInfo info = ((HasSourceInfo) node).getSourceInfo();
+ sourceInfoMap.put(new Range(start, out.getCount()), info);
+ }
+ return toReturn;
+ }
+
+ @Override
+ protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
+ for (T t : collection) {
+ doAccept(t);
+ }
+ }
+
+ @Override
+ protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
+ List<T> collection) {
+ for (T t : collection) {
+ doAccept(t);
+ }
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/js/SourceInfoHistogram.java b/dev/core/src/com/google/gwt/dev/js/SourceInfoHistogram.java
deleted file mode 100644
index 8f262c9..0000000
--- a/dev/core/src/com/google/gwt/dev/js/SourceInfoHistogram.java
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
- * Copyright 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package com.google.gwt.dev.js;
-
-import com.google.gwt.dev.jjs.Correlation;
-import com.google.gwt.dev.jjs.HasSourceInfo;
-import com.google.gwt.dev.jjs.SourceInfo;
-import com.google.gwt.dev.jjs.Correlation.Axis;
-import com.google.gwt.dev.js.ast.JsExpression;
-import com.google.gwt.dev.js.ast.JsFunction;
-import com.google.gwt.dev.js.ast.JsProgram;
-import com.google.gwt.dev.js.ast.JsValueLiteral;
-import com.google.gwt.dev.js.ast.JsVisitable;
-import com.google.gwt.dev.js.ast.JsVisitor;
-import com.google.gwt.dev.util.DefaultTextOutput;
-import com.google.gwt.dev.util.HtmlTextOutput;
-import com.google.gwt.dev.util.TextOutput;
-import com.google.gwt.dev.util.Util;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Stack;
-import java.util.TreeMap;
-import java.util.TreeSet;
-
-/**
- * This is a test reporting visitor for SOYC experiments. It will likely
- * disappear once a proper export format and viewer application are written.
- */
-public class SourceInfoHistogram {
- private static class DependencyReportVisitor extends JsVisitor {
- private final Map<Correlation, Set<Correlation>> deps = new TreeMap<Correlation, Set<Correlation>>(Correlation.AXIS_IDENT_COMPARATOR);
- private final Stack<HasSourceInfo> currentContext = new Stack<HasSourceInfo>();
-
- @Override
- protected <T extends JsVisitable<T>> T doAccept(T node) {
- /*
- * The casts to Object here are because javac 1.5.0_16 doesn't think T
- * could ever be coerced to JsNode.
- */
- boolean createScope = ((Object) node) instanceof JsProgram
- || ((Object) node) instanceof JsFunction;
-
- if (createScope) {
- currentContext.push((HasSourceInfo) node);
- }
-
- // JsValueLiterals are shared AST nodes and distort dependency info
- if (!(((Object) node) instanceof JsValueLiteral)
- && !currentContext.isEmpty()) {
- Set<Correlation> toAdd = ((HasSourceInfo) node).getSourceInfo().getAllCorrelations();
-
- HasSourceInfo context = currentContext.peek();
- for (Correlation c : context.getSourceInfo().getAllCorrelations()) {
- Set<Correlation> set = deps.get(c);
- if (set == null) {
- deps.put(c, set = new TreeSet<Correlation>(Correlation.AXIS_IDENT_COMPARATOR));
- }
- set.addAll(toAdd);
- }
- }
-
- T toReturn = super.doAccept(node);
- if (createScope) {
- currentContext.pop();
- }
-
- return toReturn;
- }
-
- @Override
- protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
- for (T node : collection) {
- doAccept(node);
- }
- }
-
- @Override
- protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
- List<T> collection) {
- for (T node : collection) {
- doAccept(node);
- }
- }
- }
-
- private static class HSVUtils {
- private static final String[] VALUES = {
- "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d",
- "e", "f"};
-
- /**
- * The number of degrees to advance around H-space.
- */
- private static final int DEGREES = 53;
- private static final double SATURATION = 0.2;
- private static final double VALUE = 0.9;
-
- /**
- * We go around and around the HSV color space to generate colors.
- *
- * @param color The index of the item to be colored.
- * @return An RGB in hex format for the color.
- */
- public static String color(int color) {
- final int h = (DEGREES * color) % 360;
- final double s = SATURATION;
- final double v = VALUE;
-
- final int hi = (int) Math.floor(h / 60.) % 6;
- final double f = (h / 60.) - hi;
- final double p = v * (1 - s);
- final double q = v * (1 - f * s);
- final double t = v * (1 - (1 - f) * s);
-
- final double r, g, b;
- switch (hi) {
- case 0:
- r = v;
- g = t;
- b = p;
- break;
- case 1:
- r = q;
- g = v;
- b = p;
- break;
- case 2:
- r = p;
- g = v;
- b = t;
- break;
- case 3:
- r = p;
- g = q;
- b = v;
- break;
- case 4:
- r = t;
- g = p;
- b = v;
- break;
- case 5:
- r = v;
- g = p;
- b = q;
- break;
- default:
- throw new RuntimeException("Unexpected hi of " + hi);
- }
-
- return numberToHex(r) + numberToHex(g) + numberToHex(b);
- }
-
- /**
- * Convert a number between 0 and 1 to a two-char hex value between 00 and
- * FF.
- */
- private static String numberToHex(double number) {
- number *= 255;
- if (number <= 0) {
- return "00";
- } else if (number >= 255) {
- return "FF";
- }
-
- String toReturn = VALUES[(int) number / 16] + VALUES[(int) number % 16];
- return toReturn;
- }
-
- /**
- * Utility class, no public constructor.
- */
- private HSVUtils() {
- }
- }
-
- private static class JavaNormalReportVisitor extends
- JsSourceGenerationVisitor {
- Stack<HasSourceInfo> stack = new Stack<HasSourceInfo>();
- int total = 0;
- private final Map<Correlation, StringBuilder> sourceByCorrelation = new TreeMap<Correlation, StringBuilder>(Correlation.AXIS_IDENT_COMPARATOR);
- private final Map<Correlation, StringBuilder> sourceByAllCorrelation = new TreeMap<Correlation, StringBuilder>(Correlation.AXIS_IDENT_COMPARATOR);
- private final SwitchTextOutput out;
-
- public JavaNormalReportVisitor(SwitchTextOutput out) {
- super(out);
- this.out = out;
- }
-
- @Override
- protected <T extends JsVisitable<T>> T doAccept(T node) {
- boolean openContext = node instanceof HasSourceInfo;
- SourceInfo sourceInfo = null;
- try {
- if (openContext) {
- sourceInfo = ((HasSourceInfo) node).getSourceInfo();
- if (!stack.isEmpty()) {
- int count = commit(stack.peek(), true);
- accumulateTotal(sourceInfo, count);
- }
- stack.push((HasSourceInfo) node);
- out.begin();
- }
- return super.doAccept(node);
- } catch (RuntimeException e) {
- e.printStackTrace();
- throw e;
- } finally {
- if (openContext) {
- int count = commit((HasSourceInfo) node, false);
- accumulateTotal(sourceInfo, count);
- if (stack.pop() != node) {
- throw new RuntimeException("Unexpected node popped");
- }
- }
- }
- }
-
- @Override
- protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
- for (T node : collection) {
- doAccept(node);
- }
- }
-
- @Override
- protected JsExpression doAcceptLvalue(JsExpression expr) {
- return doAccept(expr);
- }
-
- @Override
- protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
- List<T> collection) {
- doAcceptList(collection);
- }
-
- private void accumulateTotal(SourceInfo sourceInfo, int count) {
- total += count;
- }
-
- private int commit(HasSourceInfo x, boolean expectMore) {
- SourceInfo info = x.getSourceInfo();
- List<StringBuilder> builders = new ArrayList<StringBuilder>();
-
- // This should be an accurate count
- for (Correlation c : info.getPrimaryCorrelations()) {
- StringBuilder builder = sourceByCorrelation.get(c);
- if (builder == null) {
- builder = new StringBuilder();
- sourceByCorrelation.put(c, builder);
- }
- builders.add(builder);
- }
-
- /*
- * This intentionally overcounts base classes, methods in order to show
- * aggregate based on subtypes.
- */
- for (Correlation c : info.getAllCorrelations()) {
- StringBuilder builder = sourceByAllCorrelation.get(c);
- if (builder == null) {
- builder = new StringBuilder();
- sourceByAllCorrelation.put(c, builder);
- }
- builders.add(builder);
- }
-
- if (expectMore) {
- return out.flush(builders);
- } else {
- return out.commit(builders);
- }
- }
- }
-
- private static class JsNormalReportVisitor extends JsSourceGenerationVisitor {
- private final HtmlTextOutput out;
- private final Stack<SourceInfo> context = new Stack<SourceInfo>();
-
- public JsNormalReportVisitor(HtmlTextOutput out) {
- super(out);
- this.out = out;
- }
-
- @Override
- protected <T extends JsVisitable<T>> T doAccept(T node) {
- boolean openNode = false;
- if (node instanceof HasSourceInfo) {
- SourceInfo info = ((HasSourceInfo) node).getSourceInfo();
- openNode = context.isEmpty()
- || SourceInfo.LOCATION_COMPARATOR.compare(context.peek(), info) != 0;
- if (openNode) {
- String color;
- if (context.contains(info)) {
- color = HSVUtils.color(context.indexOf(info));
- } else {
- color = HSVUtils.color(context.size());
- }
- context.push(info);
- out.printRaw("<div class=\"node\" style=\"background:#" + color
- + ";\">");
- out.printRaw("<div class=\"story\">");
- out.print(info.getStory());
- out.printRaw("</div>");
- }
- }
- T toReturn = super.doAccept(node);
- if (openNode) {
- out.printRaw("</div>");
- context.pop();
- }
- return toReturn;
- }
-
- @Override
- protected <T extends JsVisitable<T>> void doAcceptList(List<T> collection) {
- for (T node : collection) {
- doAccept(node);
- }
- }
-
- @Override
- protected JsExpression doAcceptLvalue(JsExpression expr) {
- return doAccept(expr);
- }
-
- @Override
- protected <T extends JsVisitable<T>> void doAcceptWithInsertRemove(
- List<T> collection) {
- doAcceptList(collection);
- }
- }
-
- private static class SwitchTextOutput implements TextOutput {
- private Stack<DefaultTextOutput> outs = new Stack<DefaultTextOutput>();
-
- public void begin() {
- outs.push(new DefaultTextOutput(true));
- }
-
- public int commit(Collection<StringBuilder> builders) {
- String string = outs.pop().toString();
- for (StringBuilder builder : builders) {
- builder.append(string);
- }
- return string.length();
- }
-
- public int flush(Collection<StringBuilder> builders) {
- int toReturn = commit(builders);
- begin();
- return toReturn;
- }
-
- public void indentIn() {
- // outs.peek().indentIn();
- }
-
- public void indentOut() {
- // outs.peek().indentOut();
- }
-
- public void newline() {
- outs.peek().newline();
- }
-
- public void newlineOpt() {
- outs.peek().newlineOpt();
- }
-
- public void print(char c) {
- outs.peek().print(c);
- }
-
- public void print(char[] s) {
- outs.peek().print(s);
- }
-
- public void print(String s) {
- outs.peek().print(s);
- }
-
- public void printOpt(char c) {
- outs.peek().printOpt(c);
- }
-
- public void printOpt(char[] s) {
- outs.peek().printOpt(s);
- }
-
- public void printOpt(String s) {
- outs.peek().printOpt(s);
- }
- }
-
- public static void exec(JsProgram program, String outputPath) {
- writeDependencyReport(program, outputPath);
- writeJavaNormalReport(program, outputPath);
- writeJsNormalReport(program, outputPath);
- }
-
- private static void writeDependencyReport(JsProgram program, String outputPath) {
- DependencyReportVisitor v = new DependencyReportVisitor();
- v.accept(program);
-
- Map<Correlation, Integer> idents = new HashMap<Correlation, Integer>();
- TreeMap<String, Set<Integer>> clusters = new TreeMap<String, Set<Integer>>();
- for (Correlation c : v.deps.keySet()) {
- if (c.getAxis().equals(Axis.CLASS)) {
- clusters.put(c.getIdent(), new TreeSet<Integer>());
- }
- }
-
- EnumSet<Axis> toShow = EnumSet.of(Axis.METHOD, Axis.FIELD);
-
- StringBuilder edges = new StringBuilder();
- StringBuilder nodes = new StringBuilder();
- StringBuilder subgraphs = new StringBuilder();
-
- for (Map.Entry<Correlation, Set<Correlation>> entry : v.deps.entrySet()) {
- Correlation key = entry.getKey();
-
- if (!toShow.contains(key.getAxis())
- || key.getIdent().startsWith("java.lang")) {
- continue;
- }
-
- Set<Integer> keyClusterSet;
- if (!idents.containsKey(key)) {
- idents.put(key, idents.size());
- nodes.append(idents.get(key) + " [label=\"" + key + "\"];\n");
- }
- if (key.getAxis().isJava()) {
- keyClusterSet = clusters.get(clusters.headMap(key.getIdent()).lastKey());
- keyClusterSet.add(idents.get(key));
- } else {
- keyClusterSet = null;
- }
-
- for (Correlation c : entry.getValue()) {
- if (!toShow.contains(c.getAxis())
- || c.getIdent().startsWith("java.lang")) {
- continue;
- }
-
- Set<Integer> cClusterSet;
- if (!idents.containsKey(c)) {
- idents.put(c, idents.size());
- nodes.append(idents.get(c) + " [label=\"" + c + "\"];\n");
- }
- if (c.getAxis().isJava()) {
- cClusterSet = clusters.get(clusters.headMap(c.getIdent()).lastKey());
- cClusterSet.add(idents.get(c));
- } else {
- cClusterSet = null;
- }
-
- edges.append(idents.get(key) + " -> " + idents.get(c));
- if (keyClusterSet == cClusterSet) {
- edges.append(" constraint=false");
- }
- edges.append(";\n");
- }
- }
- int clusterNumber = 0;
- for (Map.Entry<String, Set<Integer>> entry : clusters.entrySet()) {
- Set<Integer> set = entry.getValue();
- if (set.isEmpty()) {
- continue;
- }
-
- subgraphs.append("subgraph cluster" + clusterNumber++ + " {");
- subgraphs.append("label=\"" + entry.getKey() + "\";");
- for (Integer i : set) {
- subgraphs.append(i + "; ");
- }
- subgraphs.append("};\n");
- }
-
- PrintWriter out;
- try {
- File outputPathDir = new File(outputPath);
- outputPathDir.mkdirs();
- out = new PrintWriter(new FileWriter(File.createTempFile("soyc",
- "-deps.dot", outputPathDir)));
- } catch (IOException e) {
- out = null;
- }
-
- out.println("digraph soyc {");
- out.println(subgraphs.toString());
- out.println(nodes.toString());
- out.println(edges.toString());
- out.println("}");
- out.close();
- }
-
- private static void writeJavaNormalReport(JsProgram program, String outputPath) {
- JavaNormalReportVisitor v = new JavaNormalReportVisitor(
- new SwitchTextOutput());
- v.accept(program);
-
- PrintWriter out;
- try {
- File outputPathDir = new File(outputPath);
- outputPathDir.mkdirs();
- out = new PrintWriter(new FileWriter(File.createTempFile("soyc",
- "-java.html", outputPathDir)));
- } catch (IOException e) {
- out = null;
- }
-
- out.println("<html><head>");
- out.println("<style>"
- + "* {font-family: monospace;}"
- + ".file {clear: both;}"
- + ".file * {display: none;}"
- + ".file .fileHeader, .file .fileHeader * {display: block; cursor: pointer;}"
- + ".fileOpen .fileHeader {clear: both;}"
- + ".fileOpen .javaLine {clear: both; float: left; white-space: pre; background: #efe;}"
- + ".fileOpen .jsLine {outline: thin solid black; float: right; clear: right; white-space: pre; background: #ddd;}"
- + ".story {display:none;}"
- + "div.jsLine:hover .story{display:block; position: absolute; left:0; background: #eef;}"
- + "</style>");
-
- out.println("</head><body>");
-
- out.println(String.format("<h1>Total bytes: %d</h1>", v.total));
- Map<Axis, Integer> totalsByAxis = new EnumMap<Axis, Integer>(Axis.class);
- for (Map.Entry<Correlation, StringBuilder> entry : v.sourceByCorrelation.entrySet()) {
- Correlation c = entry.getKey();
- StringBuilder builder = entry.getValue();
- int count = builder.length();
- out.println("<div class=\"file\" onclick=\"this.className=(this.className=='file'?'fileOpen':'file')\">");
- out.println("<div class=\"fileHeader\">" + Util.escapeXml(c.toString())
- + " : " + count + "</div>");
- out.print("<div class=\"jsLine\">");
- out.print(Util.escapeXml(builder.toString()));
- out.print("</div></div>");
-
- Axis axis = c.getAxis();
- Integer t = totalsByAxis.get(axis);
- if (t == null) {
- totalsByAxis.put(axis, count);
- } else {
- totalsByAxis.put(axis, t + count);
- }
- }
-
- out.println("<h1>Axis totals</h1>");
- for (Map.Entry<Axis, Integer> entry : totalsByAxis.entrySet()) {
- out.println("<div>" + entry.getKey() + " : " + entry.getValue()
- + "</div>");
- }
-
- out.println("<h1>Cost of polymorphism</h1>");
- for (Map.Entry<Correlation, StringBuilder> entry : v.sourceByAllCorrelation.entrySet()) {
- Correlation c = entry.getKey();
- StringBuilder builder = entry.getValue();
- int count = builder.length();
-
- StringBuilder uniqueOutput = v.sourceByCorrelation.get(c);
- int uniqueCount = uniqueOutput == null ? 0 : uniqueOutput.length();
- boolean bold = count != uniqueCount;
-
- out.println("<div class=\"file\" onclick=\"this.className=(this.className=='file'?'fileOpen':'file')\">");
- out.println("<div class=\"fileHeader\">" + (bold ? "<b>" : "")
- + Util.escapeXml(c.toString()) + " : " + count + " versus "
- + uniqueCount + "(" + (count - uniqueCount) + ")"
- + (bold ? "</b>" : "") + "</div>");
- out.print("<div class=\"jsLine\">");
- out.print(Util.escapeXml(builder.toString()));
- out.print("</div></div>");
- }
-
- out.println("<h1>Done</h1>");
- out.println("</body></html>");
- out.close();
- }
-
- private static void writeJsNormalReport(JsProgram program, String outputPath) {
-
- PrintWriter out;
- try {
- File outputPathDir = new File(outputPath);
- outputPathDir.mkdirs();
- out = new PrintWriter(new FileWriter(File.createTempFile("soyc",
- "-js.html", outputPathDir)));
- } catch (IOException e) {
- out = null;
- }
-
- out.println("<html><head>");
- out.println("<style>" + "* {white-space: pre; font-family: monospace;}"
- + ".node {display:inline; z-index: 0;}" + ".story {display: none;}"
- + "div.node:hover > .story {"
- + " display:block; float:right; clear: right; background: inherit; "
- + " position: relative; border-left: 8px solid white; z-index: 1; max-width:50%;}"
- + "</style>");
- out.println("</head><body>");
-
- HtmlTextOutput htmlOut = new HtmlTextOutput(out, false);
- JsNormalReportVisitor v = new JsNormalReportVisitor(htmlOut);
- v.accept(program);
-
- out.println("</body></html>");
- out.close();
- }
-}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java b/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
index 1792dbe..03c3433 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsNameRef.java
@@ -136,7 +136,7 @@
if (!hasStaticRef && name != null) {
JsNode<?> staticRef = name.getStaticRef();
if (staticRef != null) {
- toReturn.addAdditonalAncestors(name.getStaticRef().getSourceInfo());
+ toReturn.copyMissingCorrelationsFrom(name.getStaticRef().getSourceInfo());
hasStaticRef = true;
}
}
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
index e6b6b9e..33dd57f 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsProgram.java
@@ -15,7 +15,10 @@
*/
package com.google.gwt.dev.js.ast;
+import com.google.gwt.dev.jjs.Correlation;
import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.Correlation.Axis;
+import com.google.gwt.dev.jjs.Correlation.Literal;
import java.util.HashMap;
import java.util.Map;
@@ -24,22 +27,30 @@
* A JavaScript program.
*/
public final class JsProgram extends JsNode<JsProgram> {
+ /**
+ * This method is used to create SourceInfos for fields in JsProgram. This
+ * method always creates a SourceInfo that has collection enabled.
+ */
+ private static SourceInfo createSourceInfoEnabled(String description) {
+ return new SourceInfoJs(-1, -1, 0, JsProgram.class.getName(), true).makeChild(
+ JsProgram.class, description);
+ }
private final JsStatement debuggerStmt = new JsDebugger(
- createSourceInfoSynthetic(JsProgram.class, "debugger statement"));
+ createSourceInfoEnabled("debugger statement"));
- private final JsEmpty emptyStmt = new JsEmpty(createSourceInfoSynthetic(
- JsProgram.class, "Empty statement"));
+ private final JsEmpty emptyStmt = new JsEmpty(
+ createSourceInfoEnabled("Empty statement"));
- private boolean enableSourceInfoDescendants;
+ private final boolean enableSourceInfoDescendants;
private final JsBooleanLiteral falseLiteral = new JsBooleanLiteral(
- createSourceInfoSynthetic(JsProgram.class, "false literal"), false);
+ createSourceInfoEnabled("false literal"), false);
private JsProgramFragment[] fragments;
private final JsNullLiteral nullLiteral = new JsNullLiteral(
- createSourceInfoSynthetic(JsProgram.class, "null literal"));
+ createSourceInfoEnabled("null literal"));
private final Map<Double, JsNumberLiteral> numberLiteralMap = new HashMap<Double, JsNumberLiteral>();
@@ -49,21 +60,42 @@
private final Map<String, JsStringLiteral> stringLiteralMap = new HashMap<String, JsStringLiteral>();
+ private final SourceInfo stringPoolSourceInfo;
+
private final JsScope topScope;
private final JsBooleanLiteral trueLiteral = new JsBooleanLiteral(
- createSourceInfoSynthetic(JsProgram.class, "true literal"), true);
+ createSourceInfoEnabled("true literal"), true);
+
+ public JsProgram() {
+ this(false);
+ }
/**
* Constructs a JavaScript program object.
+ *
+ * @param soycEnabled Controls whether or not SourceInfo nodes created via the
+ * JsProgram will record descendant information. Enabling this
+ * feature will collect extra data during the compilation cycle, but
+ * at a cost of memory and object allocations.
*/
- public JsProgram() {
+ public JsProgram(boolean soycEnabled) {
super(SourceInfoJs.INTRINSIC.makeChild(JsProgram.class,
"JavaScript program"));
+ this.enableSourceInfoDescendants = soycEnabled;
+
rootScope = new JsRootScope(this);
topScope = new JsScope(rootScope, "Global");
objectScope = new JsScope(rootScope, "Object");
setFragmentCount(1);
+ falseLiteral.getSourceInfo().addCorrelation(
+ Correlation.by(Literal.JS_BOOLEAN));
+ nullLiteral.getSourceInfo().addCorrelation(Correlation.by(Literal.JS_NULL));
+ trueLiteral.getSourceInfo().addCorrelation(
+ Correlation.by(Literal.JS_BOOLEAN));
+ stringPoolSourceInfo = createSourceInfoSynthetic(JsProgram.class,
+ "String pool");
+ stringPoolSourceInfo.addCorrelation(Correlation.by(Literal.JS_STRING));
}
public SourceInfo createSourceInfo(int lineNumber, String location) {
@@ -124,13 +156,35 @@
}
public JsNumberLiteral getNumberLiteral(double value) {
- JsNumberLiteral lit = numberLiteralMap.get(value);
- if (lit == null) {
- lit = new JsNumberLiteral(createSourceInfoSynthetic(JsProgram.class,
- "Number literal " + value), value);
- numberLiteralMap.put(value, lit);
+ return getNumberLiteral(null, value);
+ }
+
+ public JsNumberLiteral getNumberLiteral(SourceInfo info, double value) {
+ /*
+ * This method only canonicalizes number literals when we don't have an
+ * incoming SourceInfo so that we can distinguish int-0 from double-0 in the
+ * analysis.
+ */
+ if (info == null) {
+ JsNumberLiteral lit = numberLiteralMap.get(value);
+ if (lit == null) {
+ info = createSourceInfoSynthetic(JsProgram.class, "Number literal "
+ + value);
+ info.addCorrelation(Correlation.by(Literal.JS_NUMBER));
+ lit = new JsNumberLiteral(info, value);
+ numberLiteralMap.put(value, lit);
+ }
+
+ return lit;
+ } else {
+ // Only add a JS_NUMBER if no literal correlation present: e.g. Java int
+ if (info.getPrimaryCorrelation(Axis.LITERAL) == null) {
+ // Don't mutate incoming SourceInfo
+ info = info.makeChild(JsProgram.class, "Number literal " + value);
+ info.addCorrelation(Correlation.by(Literal.JS_NUMBER));
+ }
+ return new JsNumberLiteral(info, value);
}
- return lit;
}
public JsScope getObjectScope() {
@@ -162,11 +216,11 @@
public JsStringLiteral getStringLiteral(SourceInfo sourceInfo, String value) {
JsStringLiteral lit = stringLiteralMap.get(value);
if (lit == null) {
- lit = new JsStringLiteral(sourceInfo, value);
+ lit = new JsStringLiteral(stringPoolSourceInfo.makeChild(JsProgram.class,
+ "String literal: " + value), value);
stringLiteralMap.put(value, lit);
- } else {
- lit.getSourceInfo().addAdditonalAncestors(sourceInfo);
}
+ lit.getSourceInfo().merge(sourceInfo);
return lit;
}
@@ -175,18 +229,10 @@
}
public JsNameRef getUndefinedLiteral() {
- return rootScope.findExistingName("undefined").makeRef(
- createSourceInfoSynthetic(JsProgram.class, "undefined reference"));
- }
-
- /**
- * Controls whether or not SourceInfo nodes created via the JsProgram will
- * record descendant information. Enabling this feature will collect extra
- * data during the compilation cycle, but at a cost of memory and object
- * allocations.
- */
- public void setEnableSourceInfoDescendants(boolean enable) {
- enableSourceInfoDescendants = enable;
+ SourceInfo info = createSourceInfoSynthetic(JsProgram.class,
+ "undefined reference");
+ info.addCorrelation(Correlation.by(Literal.JS_UNDEFINED));
+ return rootScope.findExistingName("undefined").makeRef(info);
}
public void setFragmentCount(int fragments) {
diff --git a/dev/core/src/com/google/gwt/dev/util/FileBackedObject.java b/dev/core/src/com/google/gwt/dev/util/FileBackedObject.java
new file mode 100644
index 0000000..af62425
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/FileBackedObject.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Represents a File that contains the serialized form of a Serializable object.
+ *
+ * @param <T> the type of object serialized into the file
+ */
+public class FileBackedObject<T extends Serializable> implements Serializable {
+ private final File backingFile;
+ private final Class<T> clazz;
+
+ /**
+ * Constructs an empty FileBackedObject. A temporary File will be used and
+ * this file will be deleted when the JVM exits.
+ *
+ * @param clazz the type of object to be serialized
+ * @throws IOException if the temporary file could not be created
+ */
+ public FileBackedObject(Class<T> clazz) throws IOException {
+ this(clazz, File.createTempFile("fileBackedObject", ".ser"));
+ backingFile.deleteOnExit();
+ }
+
+ /**
+ * Constructs a FileBackedObject using an existing File object.
+ *
+ * @param clazz the type of object to be serialized
+ * @param backingFile the file to read from or write to
+ */
+ public FileBackedObject(Class<T> clazz, File backingFile) {
+ this.clazz = clazz;
+ this.backingFile = backingFile;
+ }
+
+ /**
+ * Returns the underlying File object.
+ */
+ public File getFile() {
+ return backingFile;
+ }
+
+ /**
+ * Construct a new instance of the object stored in the backing file.
+ *
+ * @param logger a sink for error messages
+ * @return a new instance of the object stored in the backing file
+ * @throws UnableToCompleteException if the backing store does not contain an
+ * object of type <code>T</code>
+ */
+ public T newInstance(TreeLogger logger) throws UnableToCompleteException {
+ try {
+ T toReturn = Util.readFileAsObject(backingFile, clazz);
+ if (toReturn == null) {
+ logger.log(TreeLogger.ERROR, "Unable to instantiate object");
+ throw new UnableToCompleteException();
+ }
+ return toReturn;
+ } catch (ClassNotFoundException e) {
+ logger.log(TreeLogger.ERROR, "Missing class definition", e);
+ throw new UnableToCompleteException();
+ }
+ }
+
+ /**
+ * Set the contents of the backing file.
+ *
+ * @param logger a sink for error messages
+ * @param object the object to store
+ * @throws UnableToCompleteException if the object could not be serialized
+ */
+ public void set(TreeLogger logger, T object) throws IllegalStateException,
+ UnableToCompleteException {
+ assert clazz.isInstance(object);
+ Util.writeObjectAsFile(logger, backingFile, object);
+ }
+
+ @Override
+ public String toString() {
+ return backingFile.toString() + "<" + clazz.getName() + ">";
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoyc.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoyc.java
new file mode 100644
index 0000000..0bcdabd
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerSoyc.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.arg;
+
+import com.google.gwt.util.tools.ArgHandlerFlag;
+
+/**
+ * An ArgHandler that enables Story Of Your Compile data-collection.
+ */
+public class ArgHandlerSoyc extends ArgHandlerFlag {
+
+ private final OptionSoycEnabled options;
+
+ public ArgHandlerSoyc(OptionSoycEnabled options) {
+ this.options = options;
+ }
+
+ @Override
+ public String getPurpose() {
+ return "Enable Story Of Your Compile";
+ }
+
+ @Override
+ public String getTag() {
+ return "-soyc";
+ }
+
+ @Override
+ public boolean setFlag() {
+ options.setSoycEnabled(true);
+ return true;
+ }
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionSoycEnabled.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionSoycEnabled.java
new file mode 100644
index 0000000..3e81c37
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionSoycEnabled.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.dev.util.arg;
+
+/**
+ * Option for enabling Story Of Your Compile data-collection.
+ */
+public interface OptionSoycEnabled {
+ boolean isSoycEnabled();
+
+ void setSoycEnabled(boolean enabled);
+}
diff --git a/doc/build.xml b/doc/build.xml
index 6e81eec..6eed4cb 100644
--- a/doc/build.xml
+++ b/doc/build.xml
@@ -10,7 +10,7 @@
<property.ensure name="gwt.dev.jar" location="${gwt.build.lib}/gwt-dev-linux.jar" />
<property name="USER_PKGS"
- value="com.google.gwt.core.client;com.google.gwt.core.ext;com.google.gwt.core.ext.linker;com.google.gwt.core.ext.typeinfo;com.google.gwt.dom.client;com.google.gwt.i18n.client;com.google.gwt.i18n.rebind.format;com.google.gwt.i18n.rebind.keygen;com.google.gwt.json.client;com.google.gwt.junit.client;com.google.gwt.benchmarks.client;com.google.gwt.user.client;com.google.gwt.user.client.rpc;com.google.gwt.user.client.ui;com.google.gwt.user.server.rpc;com.google.gwt.xml.client;com.google.gwt.http.client;com.google.gwt.animation.client" />
+ value="com.google.gwt.core.client;com.google.gwt.core.ext;com.google.gwt.core.ext.soyc;com.google.gwt.core.ext.linker;com.google.gwt.core.ext.typeinfo;com.google.gwt.dom.client;com.google.gwt.i18n.client;com.google.gwt.i18n.rebind.format;com.google.gwt.i18n.rebind.keygen;com.google.gwt.json.client;com.google.gwt.junit.client;com.google.gwt.benchmarks.client;com.google.gwt.user.client;com.google.gwt.user.client.rpc;com.google.gwt.user.client.ui;com.google.gwt.user.server.rpc;com.google.gwt.xml.client;com.google.gwt.http.client;com.google.gwt.animation.client" />
<property name="LANG_PKGS" value="java.lang;java.lang.annotation;java.util;java.io;java.sql" />
<!-- Individual classes to include when we don't want to
diff --git a/user/src/com/google/gwt/core/Core.gwt.xml b/user/src/com/google/gwt/core/Core.gwt.xml
index fa52b58..7448eca 100644
--- a/user/src/com/google/gwt/core/Core.gwt.xml
+++ b/user/src/com/google/gwt/core/Core.gwt.xml
@@ -25,4 +25,7 @@
<define-linker name="sso" class="com.google.gwt.core.linker.SingleScriptLinker" />
<add-linker name="std" />
+
+ <define-linker class="com.google.gwt.core.linker.soyc.SoycReportLinker" name="soycReport" />
+ <add-linker name="soycReport" />
</module>