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>