Merging from releases/1.6@3739:3876 svn merge -r3739:HEAD \ https://google-web-toolkit.googlecode.com/svn/releases/1.6 . Extensive merge conflicts in JavaToJavaScriptCompiler were resolved by disabling SOYC -- BobV will re-enable it with his upcoming commit. A patch for StandardGeneratorContext is also included to avoid a build break, which will be committed to releases/1.6 at the same time branch-info is updated, and will be skipped in the revision for the next merge. git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@3877 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java b/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java index f5fe286..a54eaea 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/Artifact.java
@@ -17,6 +17,8 @@ import com.google.gwt.core.ext.Linker; +import java.io.Serializable; + /** * A base type for all artifacts relating to the link process. In order to * ensure stable output between runs of the compiler, Artifact types must @@ -27,8 +29,9 @@ * to. */ public abstract class Artifact<C extends Artifact<C>> implements - Comparable<Artifact<?>> { - private final Class<? extends Linker> linker; + Comparable<Artifact<?>>, Serializable { + private final String linkerName; + private transient Class<? extends Linker> linker; /** * Constructor. @@ -37,6 +40,7 @@ */ protected Artifact(Class<? extends Linker> linker) { assert linker != null; + this.linkerName = linker.getName(); this.linker = linker; } @@ -65,6 +69,17 @@ * Returns the Linker that created the Artifact. */ public final Class<? extends Linker> getLinker() { + // linker is null when deserialized. + if (linker == null) { + try { + Class<?> clazz = Class.forName(linkerName, false, + Thread.currentThread().getContextClassLoader()); + linker = clazz.asSubclass(Linker.class); + } catch (ClassNotFoundException e) { + // The class may not be available. + linker = Linker.class; + } + } return linker; }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java b/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java index c04ad6d..96435bc 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/ArtifactSet.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.core.ext.linker; +import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -25,7 +26,7 @@ /** * Provides stable ordering and de-duplication of artifacts. */ -public final class ArtifactSet implements SortedSet<Artifact<?>> { +public final class ArtifactSet implements SortedSet<Artifact<?>>, Serializable { private SortedSet<Artifact<?>> treeSet = new TreeSet<Artifact<?>>();
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java b/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java index 28c9400..3bb8000 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/GeneratedResource.java
@@ -24,11 +24,13 @@ * during the compilation process. */ public abstract class GeneratedResource extends EmittedArtifact { - private final Class<? extends Generator> generatorType; + private final String generatorTypeName; + private transient Class<? extends Generator> generatorType; protected GeneratedResource(Class<? extends Linker> linkerType, Class<? extends Generator> generatorType, String partialPath) { super(linkerType, partialPath); + this.generatorTypeName = generatorType.getName(); this.generatorType = generatorType; } @@ -36,6 +38,17 @@ * The type of Generator that created the resource. */ public final Class<? extends Generator> getGenerator() { + // generatorType is null when deserialized. + if (generatorType == null) { + try { + Class<?> clazz = Class.forName(generatorTypeName, false, + Thread.currentThread().getContextClassLoader()); + generatorType = clazz.asSubclass(Generator.class); + } catch (ClassNotFoundException e) { + // The class may not be available. + generatorType = Generator.class; + } + } return generatorType; } }
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 9a99727..e8cdb0d 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,13 +15,12 @@ */ 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.util.Util; import java.io.File; +import java.io.Serializable; import java.lang.ref.SoftReference; import java.util.Collections; import java.util.Comparator; @@ -37,11 +36,8 @@ */ public class StandardCompilationResult extends CompilationResult { - /** - * Smaller maps come before larger maps, then we compare the concatenation of - * every value. - */ - public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new Comparator<SortedMap<SelectionProperty, String>>() { + private static final class MapComparator implements + Comparator<SortedMap<SelectionProperty, String>>, Serializable { public int compare(SortedMap<SelectionProperty, String> arg0, SortedMap<SelectionProperty, String> arg1) { int diff = arg0.size() - arg1.size(); @@ -64,21 +60,25 @@ return sb0.toString().compareTo(sb1.toString()); } - }; + } + + /** + * Smaller maps come before larger maps, then we compare the concatenation of + * every value. + */ + public static final Comparator<SortedMap<SelectionProperty, String>> MAP_COMPARATOR = new MapComparator(); private final File cacheFile; - private SoftReference<String> js; + 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(TreeLogger logger, String js, - String strongName, File cacheDir) throws UnableToCompleteException { + public StandardCompilationResult(String js, String strongName, File cacheFile) { super(StandardLinkerContext.class); this.js = new SoftReference<String>(js); this.strongName = strongName; - cacheFile = new File(cacheDir, strongName); - Util.writeStringAsFile(logger, cacheFile, js); + this.cacheFile = cacheFile; } /** @@ -93,9 +93,16 @@ } public String getJavaScript() { - String toReturn = js.get(); + String toReturn = null; + if (js != null) { + toReturn = js.get(); + } if (toReturn == null) { toReturn = Util.readFileAsString(cacheFile); + if (toReturn == null) { + throw new RuntimeException("Unexpectedly unable to read JS file '" + + cacheFile.getAbsolutePath() + "'"); + } 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 fbc9675..fc6e785 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
@@ -26,7 +26,6 @@ import com.google.gwt.core.ext.linker.PublicResource; import com.google.gwt.core.ext.linker.SelectionProperty; import com.google.gwt.core.ext.linker.LinkerOrder.Order; -import com.google.gwt.dev.GWTCompiler; import com.google.gwt.dev.cfg.BindingProperty; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.Property; @@ -112,63 +111,31 @@ }; private final ArtifactSet artifacts = new ArtifactSet(); - private final File compilationsDir; + private final SortedSet<ConfigurationProperty> configurationProperties; private final JJSOptions jjsOptions; private final List<Class<? extends Linker>> linkerClasses; private final Map<Class<? extends Linker>, String> linkerShortNames = new HashMap<Class<? extends Linker>, String>(); - /** - * This is the output directory for private files. - */ - private final File moduleAuxDir; private final String moduleFunctionName; private final String moduleName; - /** - * This is the root directory for the output of a particular module's - * compilation. - */ - private final File moduleOutDir; private final Map<String, StandardSelectionProperty> propertiesByName = new HashMap<String, StandardSelectionProperty>(); private final Map<String, StandardCompilationResult> resultsByStrongName = new HashMap<String, StandardCompilationResult>(); private final SortedSet<SelectionProperty> selectionProperties; public StandardLinkerContext(TreeLogger logger, ModuleDef module, - File moduleOutDir, File generatorDir, JJSOptions jjsOptions) { + JJSOptions jjsOptions) { logger = logger.branch(TreeLogger.DEBUG, "Constructing StandardLinkerContext", null); this.jjsOptions = jjsOptions; this.moduleFunctionName = module.getFunctionName(); this.moduleName = module.getName(); - this.moduleOutDir = moduleOutDir; this.linkerClasses = new ArrayList<Class<? extends Linker>>( module.getActiveLinkers()); linkerClasses.add(module.getActivePrimaryLinker()); - if (moduleOutDir != null) { - compilationsDir = new File(moduleOutDir.getParentFile(), - GWTCompiler.GWT_COMPILER_DIR + File.separator + moduleName - + File.separator + "compilations"); - - Util.recursiveDelete(compilationsDir, true); - compilationsDir.mkdirs(); - logger.log(TreeLogger.SPAM, "compilationsDir: " - + compilationsDir.getPath(), null); - - this.moduleAuxDir = new File(moduleOutDir.getParentFile(), moduleName - + "-aux"); - if (moduleAuxDir.exists()) { - Util.recursiveDelete(moduleAuxDir, false); - } - logger.log(TreeLogger.SPAM, "mouduleAuxDir: " + moduleAuxDir.getPath(), - null); - } else { - compilationsDir = null; - moduleAuxDir = null; - } - for (Map.Entry<String, Class<? extends Linker>> entry : module.getLinkers().entrySet()) { linkerShortNames.put(entry.getValue(), entry.getKey()); } @@ -255,15 +222,19 @@ /** * Gets or creates a CompilationResult for the given JavaScript program. */ - public StandardCompilationResult getCompilation(TreeLogger logger, String js) + public StandardCompilationResult getCompilation(TreeLogger logger, File jsFile) throws UnableToCompleteException { - - byte[] bytes = Util.getBytes(js); + byte[] bytes = Util.readFileAsBytes(jsFile); + if (bytes == null) { + logger.log(TreeLogger.ERROR, "Unable to read file '" + + jsFile.getAbsolutePath() + "'"); + throw new UnableToCompleteException(); + } String strongName = Util.computeStrongName(bytes); StandardCompilationResult result = resultsByStrongName.get(strongName); if (result == null) { - result = new StandardCompilationResult(logger, js, strongName, - compilationsDir); + result = new StandardCompilationResult(Util.toString(bytes), strongName, + jsFile); resultsByStrongName.put(result.getStrongName(), result); artifacts.add(result); } @@ -295,119 +266,10 @@ return propertiesByName.get(name); } - public boolean isOutputCompact() { - return jjsOptions.getOutput().shouldMinimize(); - } - - @Override - public ArtifactSet link(TreeLogger logger, LinkerContext context, - ArtifactSet artifacts) throws UnableToCompleteException { - - logger = logger.branch(TreeLogger.INFO, "Linking compilation into " - + moduleOutDir.getPath(), null); - - artifacts = invokeLinkerStack(logger); - - for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) { - TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG, - "Emitting resource " + artifact.getPartialPath(), null); - - File outFile; - if (artifact.isPrivate()) { - outFile = new File(getLinkerAuxDir(artifact.getLinker()), - artifact.getPartialPath()); - } else { - outFile = new File(moduleOutDir, artifact.getPartialPath()); - } - - assert !outFile.exists() : "Attempted to overwrite " + outFile.getPath(); - Util.copy(logger, artifact.getContents(artifactLogger), outFile); - } - - return artifacts; - } - - public String optimizeJavaScript(TreeLogger logger, String program) - throws UnableToCompleteException { - logger = logger.branch(TreeLogger.DEBUG, "Attempting to optimize JS", null); - JsParser parser = new JsParser(); - Reader r = new StringReader(program); - JsProgram jsProgram = new JsProgram(); - JsScope topScope = jsProgram.getScope(); - JsName funcName = topScope.declareName(getModuleFunctionName()); - funcName.setObfuscatable(false); - - try { - SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic( - StandardLinkerContext.class, "Linker-derived JS"); - parser.setSourceInfo(sourceInfo); - parser.parseInto(topScope, jsProgram.getGlobalBlock(), r, 1); - } catch (IOException e) { - logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e); - throw new UnableToCompleteException(); - } catch (JsParserException e) { - logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e); - throw new UnableToCompleteException(); - } - - JsSymbolResolver.exec(jsProgram); - JsUnusedFunctionRemover.exec(jsProgram); - - switch (jjsOptions.getOutput()) { - case OBFUSCATED: - /* - * We can't apply the regular JsStringInterner to the JsProgram that - * we've just created. In the normal case, the JsStringInterner adds an - * additional statement to the program's global JsBlock, however we - * don't know exactly what the form and structure of our JsProgram are, - * so we'll limit the scope of the modifications to each top-level - * function within the program. - */ - TopFunctionStringInterner.exec(jsProgram); - JsObfuscateNamer.exec(jsProgram); - break; - case PRETTY: - // We don't intern strings in pretty mode to improve readability - JsPrettyNamer.exec(jsProgram); - break; - case DETAILED: - // As above with OBFUSCATED - TopFunctionStringInterner.exec(jsProgram); - JsVerboseNamer.exec(jsProgram); - break; - default: - throw new InternalCompilerException("Unknown output mode"); - } - - DefaultTextOutput out = new DefaultTextOutput( - jjsOptions.getOutput().shouldMinimize()); - JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); - v.accept(jsProgram); - return out.toString(); - } - - /** - * Creates a linker-specific subdirectory in the module's auxiliary output - * directory. - */ - private File getLinkerAuxDir(Class<? extends Linker> linkerType) { - // The auxiliary directory is create lazily - if (!moduleAuxDir.exists()) { - moduleAuxDir.mkdirs(); - } - assert linkerShortNames.containsKey(linkerType) : linkerType.getName() - + " unknown"; - File toReturn = new File(moduleAuxDir, linkerShortNames.get(linkerType)); - if (!toReturn.exists()) { - toReturn.mkdirs(); - } - return toReturn; - } - /** * Run the linker stack. */ - private ArtifactSet invokeLinkerStack(TreeLogger logger) + public ArtifactSet invokeLinkerStack(TreeLogger logger) throws UnableToCompleteException { ArtifactSet workingArtifacts = new ArtifactSet(artifacts); Stack<Linker> linkerStack = new Stack<Linker>(); @@ -480,4 +342,115 @@ return workingArtifacts; } + + public boolean isOutputCompact() { + return jjsOptions.getOutput().shouldMinimize(); + } + + @Override + public ArtifactSet link(TreeLogger logger, LinkerContext context, + ArtifactSet artifacts) throws UnableToCompleteException { + throw new UnsupportedOperationException(); + } + + public String optimizeJavaScript(TreeLogger logger, String program) + throws UnableToCompleteException { + logger = logger.branch(TreeLogger.DEBUG, "Attempting to optimize JS", null); + JsParser parser = new JsParser(); + Reader r = new StringReader(program); + JsProgram jsProgram = new JsProgram(); + JsScope topScope = jsProgram.getScope(); + JsName funcName = topScope.declareName(getModuleFunctionName()); + funcName.setObfuscatable(false); + + try { + SourceInfo sourceInfo = jsProgram.createSourceInfoSynthetic( + StandardLinkerContext.class, "Linker-derived JS"); + parser.setSourceInfo(sourceInfo); + parser.parseInto(topScope, jsProgram.getGlobalBlock(), r, 1); + } catch (IOException e) { + logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e); + throw new UnableToCompleteException(); + } catch (JsParserException e) { + logger.log(TreeLogger.ERROR, "Unable to parse JavaScript", e); + throw new UnableToCompleteException(); + } + + JsSymbolResolver.exec(jsProgram); + JsUnusedFunctionRemover.exec(jsProgram); + + switch (jjsOptions.getOutput()) { + case OBFUSCATED: + /* + * We can't apply the regular JsStringInterner to the JsProgram that + * we've just created. In the normal case, the JsStringInterner adds an + * additional statement to the program's global JsBlock, however we + * don't know exactly what the form and structure of our JsProgram are, + * so we'll limit the scope of the modifications to each top-level + * function within the program. + */ + TopFunctionStringInterner.exec(jsProgram); + JsObfuscateNamer.exec(jsProgram); + break; + case PRETTY: + // We don't intern strings in pretty mode to improve readability + JsPrettyNamer.exec(jsProgram); + break; + case DETAILED: + // As above with OBFUSCATED + TopFunctionStringInterner.exec(jsProgram); + JsVerboseNamer.exec(jsProgram); + break; + default: + throw new InternalCompilerException("Unknown output mode"); + } + + DefaultTextOutput out = new DefaultTextOutput( + jjsOptions.getOutput().shouldMinimize()); + JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); + v.accept(jsProgram); + return out.toString(); + } + + public void produceOutputDirectory(TreeLogger logger, ArtifactSet artifacts, + File moduleOutDir, File moduleAuxDir) throws UnableToCompleteException { + + logger = logger.branch(TreeLogger.INFO, "Linking compilation into " + + moduleOutDir.getPath(), null); + + for (EmittedArtifact artifact : artifacts.find(EmittedArtifact.class)) { + TreeLogger artifactLogger = logger.branch(TreeLogger.DEBUG, + "Emitting resource " + artifact.getPartialPath(), null); + + File outFile; + if (artifact.isPrivate()) { + outFile = new File(getLinkerAuxDir(moduleAuxDir, artifact.getLinker()), + artifact.getPartialPath()); + } else { + outFile = new File(moduleOutDir, artifact.getPartialPath()); + } + + assert !outFile.exists() : "Attempted to overwrite " + outFile.getPath(); + Util.copy(logger, artifact.getContents(artifactLogger), outFile); + } + } + + /** + * Creates a linker-specific subdirectory in the module's auxiliary output + * directory. + */ + private File getLinkerAuxDir(File moduleAuxDir, + Class<? extends Linker> linkerType) { + // The auxiliary directory is create lazily + if (!moduleAuxDir.exists()) { + moduleAuxDir.mkdirs(); + } + assert linkerShortNames.containsKey(linkerType) : linkerType.getName() + + " unknown"; + File toReturn = new File(moduleAuxDir, linkerShortNames.get(linkerType)); + if (!toReturn.exists()) { + toReturn.mkdirs(); + } + return toReturn; + } }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java index 401b1b8..2a1a0bd 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardPublicResource.java
@@ -19,13 +19,37 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.linker.PublicResource; import com.google.gwt.dev.resource.Resource; +import com.google.gwt.dev.util.Util; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.InputStream; +import java.io.Serializable; /** * The standard implementation of {@link PublicResource}. */ public class StandardPublicResource extends PublicResource { + + /** + * Serializes a public resource via a snapshot of the content. + */ + private static final class SerializedPublicResource extends PublicResource { + private final byte[] data; + + protected SerializedPublicResource(String partialPath, byte[] data) { + super(StandardLinkerContext.class, partialPath); + this.data = data; + } + + @Override + public InputStream getContents(TreeLogger logger) + throws UnableToCompleteException { + return new ByteArrayInputStream(data); + } + } + private final Resource resource; public StandardPublicResource(String partialPath, Resource resource) { @@ -38,4 +62,18 @@ throws UnableToCompleteException { return resource.openContents(); } + + private Object writeReplace() { + if (resource instanceof Serializable) { + return this; + } + // Resource is not serializable, must replace myself. + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + Util.copy(resource.openContents(), baos); + return new SerializedPublicResource(getPartialPath(), baos.toByteArray()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } }
diff --git a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java index 14972c1..5f71786 100644 --- a/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java +++ b/dev/core/src/com/google/gwt/core/ext/linker/impl/StandardSelectionProperty.java
@@ -40,8 +40,7 @@ activeValue = null; } name = p.getName(); - provider = p.getProvider() == null ? null - : p.getProvider().getBody().toSource(); + provider = p.getProvider() == null ? null : p.getProvider().getBody(); values = Collections.unmodifiableSortedSet(new TreeSet<String>( Arrays.asList(p.getDefinedValues()))); }
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java new file mode 100644 index 0000000..17b1042 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -0,0 +1,267 @@ +/* + * 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.CompileTaskRunner.CompileTask; +import com.google.gwt.dev.PermutationCompiler.ResultsHandler; +import com.google.gwt.dev.jjs.UnifiedAst; +import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler; +import com.google.gwt.dev.util.Util; +import com.google.gwt.util.tools.ArgHandlerString; + +import java.io.File; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Performs the first phase of compilation, generating the set of permutations + * to compile, and a ready-to-compile AST. + */ +public class CompilePerms { + + /** + * Options for CompilePerms. + */ + public interface CompilePermsOptions extends CompileTaskOptions, OptionPerms { + } + + /** + * Handles options for which permutations to compile. + */ + public interface OptionPerms { + /** + * Gets the ordered set of permutations to compile. Returns a zero-length + * array if all permutations should be compiled. + */ + int[] getPermsToCompile(); + + /** + * Adds another permutation to compile. + */ + void setPermsToCompile(int[] permsToCompile); + } + + /** + * Argument handler for specifying the which perms to run. + */ + protected static final class ArgHandlerPerms extends ArgHandlerString { + private final OptionPerms option; + + public ArgHandlerPerms(OptionPerms option) { + this.option = option; + } + + @Override + public String getPurpose() { + return "Comma-delimited list of 0-based permutations to compile"; + } + + @Override + public String getTag() { + return "-perms"; + } + + @Override + public String[] getTagArgs() { + return new String[] {"permlist"}; + } + + @Override + public boolean setString(String str) { + String[] split = str.split(","); + if (split.length < 1) { + System.err.println(getTag() + + " requires a comma-delimited list of integers"); + return false; + } + + SortedSet<Integer> permSet = new TreeSet<Integer>(); + for (String item : split) { + try { + int value = Integer.parseInt(item); + if (value < 0) { + System.err.println(getTag() + " error: negative value '" + value + + "' is not allowed"); + return false; + } + permSet.add(value); + } catch (NumberFormatException e) { + System.err.println(getTag() + + " requires a comma-delimited list of integers; '" + item + + "' is not an integer"); + return false; + } + } + int[] permsToCompile = new int[permSet.size()]; + int i = 0; + for (int perm : permSet) { + permsToCompile[i++] = perm; + } + option.setPermsToCompile(permsToCompile); + return true; + } + } + static class ArgProcessor extends Link.ArgProcessor { + public ArgProcessor(CompilePermsOptions options) { + super(options); + registerHandler(new ArgHandlerPerms(options)); + } + + @Override + protected String getName() { + return CompilePerms.class.getName(); + } + } + + /** + * Concrete class to implement all compiler options. + */ + static class CompilePermsOptionsImpl extends CompileTaskOptionsImpl implements + CompilePermsOptions { + + private int[] permsToCompile = new int[0]; + + public CompilePermsOptionsImpl() { + } + + public CompilePermsOptionsImpl(CompilePermsOptions other) { + copyFrom(other); + } + + public void copyFrom(CompilePermsOptions other) { + super.copyFrom(other); + + setPermsToCompile(other.getPermsToCompile()); + } + + public int[] getPermsToCompile() { + return permsToCompile.clone(); + } + + public void setPermsToCompile(int[] permsToCompile) { + this.permsToCompile = permsToCompile.clone(); + } + } + + public static String compile(TreeLogger logger, Permutation permutation, + UnifiedAst unifiedAst) { + try { + 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. + return null; + } + } + + public static void main(String[] args) { + /* + * NOTE: main always exits with a call to System.exit to terminate any + * non-daemon threads that were started in Generators. Typically, this is to + * shutdown AWT related threads, since the contract for their termination is + * still implementation-dependent. + */ + final CompilePermsOptions options = new CompilePermsOptionsImpl(); + if (new ArgProcessor(options).processArgs(args)) { + CompileTask task = new CompileTask() { + public boolean run(TreeLogger logger) throws UnableToCompleteException { + return new CompilePerms(options).run(logger); + } + }; + if (CompileTaskRunner.runWithAppropriateLogger(options, task)) { + // Exit w/ success code. + System.exit(0); + } + } + // Exit w/ non-success code. + System.exit(1); + } + + /** + * Return the filename corresponding to the given permutation number, + * one-based. + */ + static File makePermFilename(File compilerWorkDir, int permNumber) { + return new File(compilerWorkDir, "permutation-" + permNumber + ".js"); + } + + private final CompilePermsOptionsImpl options; + + public CompilePerms(CompilePermsOptions options) { + this.options = new CompilePermsOptionsImpl(options); + } + + public boolean run(TreeLogger logger) throws UnableToCompleteException { + File precompilationFile = new File(options.getCompilerWorkDir(), + Precompile.PRECOMPILATION_FILENAME); + if (!precompilationFile.exists()) { + logger.log(TreeLogger.ERROR, "File not found '" + + precompilationFile.getAbsolutePath() + + "'; please run Precompile first"); + return false; + } + Precompilation precompilation; + try { + /* + * TODO: don't bother deserializing the generated artifacts. + */ + precompilation = Util.readFileAsObject(precompilationFile, + Precompilation.class); + } catch (ClassNotFoundException e) { + logger.log(TreeLogger.ERROR, "Unable to deserialize '" + + precompilationFile.getAbsolutePath() + "'", e); + return false; + } + Permutation[] perms = precompilation.getPermutations(); + UnifiedAst unifiedAst = precompilation.getUnifiedAst(); + int[] permsToRun = options.getPermsToCompile(); + + if (permsToRun.length == 0) { + // Compile them all. + permsToRun = new int[perms.length]; + for (int i = 0; i < permsToRun.length; ++i) { + permsToRun[i] = i; + } + } else { + // Range check the supplied perms. + for (int permToRun : permsToRun) { + if (permToRun >= perms.length) { + logger.log(TreeLogger.ERROR, "The specified perm number '" + + permToRun + "' is too big; the maximum value is " + + (perms.length - 1) + "'"); + return false; + } + } + } + + final TreeLogger branch = logger.branch(TreeLogger.INFO, "Compiling " + + permsToRun.length + " permutations"); + PermutationCompiler multiThread = new PermutationCompiler(branch, + unifiedAst, perms, permsToRun); + multiThread.go(new ResultsHandler() { + public void addResult(Permutation permutation, int permNum, String js) + throws UnableToCompleteException { + Util.writeStringAsFile(branch, makePermFilename( + options.getCompilerWorkDir(), permNum), js); + } + }); + branch.log(TreeLogger.INFO, "Permutation compile succeeded"); + return true; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java b/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java new file mode 100644 index 0000000..dd345fc --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/CompileTaskOptions.java
@@ -0,0 +1,28 @@ +/* + * 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.dev.util.arg.OptionGuiLogger; +import com.google.gwt.dev.util.arg.OptionLogLevel; +import com.google.gwt.dev.util.arg.OptionModuleName; +import com.google.gwt.dev.util.arg.OptionOutDir; + +/** + * A common set of options for all compile tasks. + */ +public interface CompileTaskOptions extends OptionGuiLogger, OptionModuleName, + OptionLogLevel, OptionOutDir { +}
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java b/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java new file mode 100644 index 0000000..b2f0295 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/CompileTaskOptionsImpl.java
@@ -0,0 +1,90 @@ +/* + * 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.Type; + +import java.io.File; + +/** + * Concrete class to implement compiler task options. + */ +class CompileTaskOptionsImpl implements CompileTaskOptions { + + public static final String GWT_COMPILER_DIR = ".gwt-tmp" + File.separatorChar + + "compiler"; + + private File compilerWorkDir; + private Type logLevel; + private String moduleName; + private File outDir; + private boolean useGuiLogger; + + public CompileTaskOptionsImpl() { + } + + public CompileTaskOptionsImpl(CompileTaskOptions other) { + copyFrom(other); + } + + public void copyFrom(CompileTaskOptions other) { + setLogLevel(other.getLogLevel()); + setModuleName(other.getModuleName()); + setOutDir(other.getOutDir()); + setUseGuiLogger(other.isUseGuiLogger()); + } + + public File getCompilerWorkDir() { + if (compilerWorkDir == null) { + compilerWorkDir = new File(getOutDir(), GWT_COMPILER_DIR + File.separator + + moduleName); + } + return compilerWorkDir; + } + + public Type getLogLevel() { + return logLevel; + } + + public String getModuleName() { + return moduleName; + } + + public File getOutDir() { + return outDir; + } + + public boolean isUseGuiLogger() { + return useGuiLogger; + } + + public void setLogLevel(Type logLevel) { + this.logLevel = logLevel; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public void setOutDir(File outDir) { + this.outDir = outDir; + } + + public void setUseGuiLogger(boolean useGuiLogger) { + this.useGuiLogger = useGuiLogger; + } + +} \ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java b/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java new file mode 100644 index 0000000..a5efcfb --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/CompileTaskRunner.java
@@ -0,0 +1,119 @@ +/* + * 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.util.log.AbstractTreeLogger; +import com.google.gwt.dev.util.log.DetachedTreeLoggerWindow; +import com.google.gwt.dev.util.log.PrintWriterTreeLogger; + +/** + * Used to run compiler tasks with the appropriate logger. + */ +public class CompileTaskRunner { + + /** + * A task to run with a logger based on options. + */ + public interface CompileTask { + boolean run(TreeLogger logger) throws UnableToCompleteException; + } + + /** + * Runs the main action with an appropriate logger. If a gui-based TreeLogger + * is used, this method will not return until its window is closed by the + * user. + */ + public static boolean runWithAppropriateLogger(CompileTaskOptions options, + final CompileTask task) { + // Set any platform specific system properties. + BootStrapPlatform.applyPlatformHacks(); + + if (options.isUseGuiLogger()) { + // Initialize a tree logger window. + DetachedTreeLoggerWindow loggerWindow = DetachedTreeLoggerWindow.getInstance( + "Build Output for " + options.getModuleName(), 800, 600, true); + + // Eager AWT initialization for OS X to ensure safe coexistence with SWT. + BootStrapPlatform.maybeInitializeAWT(); + + final AbstractTreeLogger logger = loggerWindow.getLogger(); + logger.setMaxDetail(options.getLogLevel()); + final boolean[] success = new boolean[1]; + + // Compiler will be spawned onto a second thread, UI thread for tree + // logger will remain on the main. + Thread compilerThread = new Thread(new Runnable() { + public void run() { + success[0] = doRun(logger, task); + } + }); + + compilerThread.setName("GWTCompiler Thread"); + compilerThread.start(); + loggerWindow.run(); + + // Even if the tree logger window is closed, we wait for the compiler + // to finish. + waitForThreadToTerminate(compilerThread); + + return success[0]; + } else { + PrintWriterTreeLogger logger = new PrintWriterTreeLogger(); + logger.setMaxDetail(options.getLogLevel()); + return doRun(logger, task); + } + } + + private static boolean doRun(TreeLogger logger, CompileTask task) { + try { + return task.run(logger); + } catch (UnableToCompleteException e) { + // Assume logged. + } catch (Throwable e) { + logger.log(TreeLogger.ERROR, "Unexpected", e); + } + return false; + } + + /** + * Waits for a thread to terminate before it returns. This method is a + * non-cancellable task, in that it will defer thread interruption until it is + * done. + * + * @param godot the thread that is being waited on. + */ + private static void waitForThreadToTerminate(final Thread godot) { + // Goetz pattern for non-cancellable tasks. + // http://www-128.ibm.com/developerworks/java/library/j-jtp05236.html + boolean isInterrupted = false; + try { + while (true) { + try { + godot.join(); + return; + } catch (InterruptedException e) { + isInterrupted = true; + } + } + } finally { + if (isInterrupted) { + Thread.currentThread().interrupt(); + } + } + } +}
diff --git a/dev/core/src/com/google/gwt/dev/CompilerOptions.java b/dev/core/src/com/google/gwt/dev/CompilerOptions.java new file mode 100644 index 0000000..85b4afc --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/CompilerOptions.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.dev; + +import com.google.gwt.dev.jjs.JJSOptions; +import com.google.gwt.dev.util.arg.OptionGenDir; +import com.google.gwt.dev.util.arg.OptionValidateOnly; + +/** + * The complete set of options for the GWT compiler. + */ +public interface CompilerOptions extends JJSOptions, CompileTaskOptions, + OptionGenDir, OptionValidateOnly { +}
diff --git a/dev/core/src/com/google/gwt/dev/CompilerOptionsImpl.java b/dev/core/src/com/google/gwt/dev/CompilerOptionsImpl.java new file mode 100644 index 0000000..dcc29bf --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/CompilerOptionsImpl.java
@@ -0,0 +1,98 @@ +/* + * 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.Type; +import com.google.gwt.dev.jjs.JJSOptionsImpl; + +import java.io.File; + +/** + * Concrete class to implement all compiler options. + */ +public class CompilerOptionsImpl extends JJSOptionsImpl implements + CompilerOptions { + private File genDir; + private Type logLevel; + private String moduleName; + private File outDir; + private boolean useGuiLogger; + private boolean validateOnly; + + public CompilerOptionsImpl() { + } + + public CompilerOptionsImpl(CompilerOptions other) { + copyFrom(other); + } + + public void copyFrom(CompilerOptions other) { + super.copyFrom(other); + setGenDir(other.getGenDir()); + setLogLevel(other.getLogLevel()); + setOutDir(other.getOutDir()); + setUseGuiLogger(other.isUseGuiLogger()); + setValidateOnly(false); + } + + public File getGenDir() { + return genDir; + } + + public Type getLogLevel() { + return logLevel; + } + + public String getModuleName() { + return moduleName; + } + + public File getOutDir() { + return outDir; + } + + public boolean isUseGuiLogger() { + return useGuiLogger; + } + + public boolean isValidateOnly() { + return validateOnly; + } + + public void setGenDir(File genDir) { + this.genDir = genDir; + } + + public void setLogLevel(Type logLevel) { + this.logLevel = logLevel; + } + + public void setModuleName(String moduleName) { + this.moduleName = moduleName; + } + + public void setOutDir(File outDir) { + this.outDir = outDir; + } + + public void setUseGuiLogger(boolean useGuiLogger) { + this.useGuiLogger = useGuiLogger; + } + + public void setValidateOnly(boolean validateOnly) { + this.validateOnly = validateOnly; + } +} \ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java index b11b457..3f89bfd 100644 --- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java +++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -17,173 +17,26 @@ 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.core.ext.linker.ArtifactSet; -import com.google.gwt.core.ext.linker.impl.StandardLinkerContext; -import com.google.gwt.dev.cfg.BindingProperty; -import com.google.gwt.dev.cfg.ConfigurationProperty; -import com.google.gwt.dev.cfg.ModuleDef; -import com.google.gwt.dev.cfg.ModuleDefLoader; -import com.google.gwt.dev.cfg.PropertyPermutations; -import com.google.gwt.dev.cfg.Rules; -import com.google.gwt.dev.cfg.StaticPropertyOracle; -import com.google.gwt.dev.javac.CompilationState; -import com.google.gwt.dev.javac.CompilationUnit; -import com.google.gwt.dev.jdt.RebindOracle; -import com.google.gwt.dev.jdt.RebindPermutationOracle; -import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd; -import com.google.gwt.dev.jjs.JJSOptions; -import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler; -import com.google.gwt.dev.jjs.JsOutputOption; -import com.google.gwt.dev.shell.StandardRebindOracle; -import com.google.gwt.dev.util.PerfLogger; -import com.google.gwt.dev.util.Util; -import com.google.gwt.dev.util.arg.ArgHandlerGenDir; -import com.google.gwt.dev.util.arg.ArgHandlerLogLevel; -import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle; -import com.google.gwt.dev.util.arg.ArgHandlerTreeLoggerFlag; -import com.google.gwt.dev.util.log.AbstractTreeLogger; -import com.google.gwt.dev.util.log.DetachedTreeLoggerWindow; -import com.google.gwt.dev.util.log.PrintWriterTreeLogger; -import com.google.gwt.util.tools.ArgHandlerDisableAggressiveOptimization; -import com.google.gwt.util.tools.ArgHandlerEnableAssertions; -import com.google.gwt.util.tools.ArgHandlerExtra; -import com.google.gwt.util.tools.ArgHandlerFlag; -import com.google.gwt.util.tools.ArgHandlerOutDir; -import com.google.gwt.util.tools.ArgHandlerString; -import com.google.gwt.util.tools.ToolBase; - -import java.io.File; -import java.util.HashSet; -import java.util.Set; -import java.util.SortedSet; +import com.google.gwt.dev.CompilePerms.CompilePermsOptionsImpl; +import com.google.gwt.dev.CompileTaskRunner.CompileTask; +import com.google.gwt.dev.Precompile.CompilerOptionsImpl; /** * The main executable entry point for the GWT Java to JavaScript compiler. */ -public class GWTCompiler extends ToolBase { +public class GWTCompiler { - private class ArgHandlerModuleName extends ArgHandlerExtra { - - @Override - public boolean addExtraArg(String arg) { - setModuleName(arg); - return true; + static final class ArgProcessor extends Precompile.ArgProcessor { + public ArgProcessor(CompilerOptions options) { + super(options); } @Override - public String getPurpose() { - return "Specifies the name of the module to compile"; - } - - @Override - public String[] getTagArgs() { - return new String[] {"module"}; - } - - @Override - public boolean isRequired() { - return true; + protected String getName() { + return GWTCompiler.class.getName(); } } - private class ArgHandlerStoryOfYourCompile extends ArgHandlerString { - @Override - public String getPurpose() { - return "Generate the story of your compile"; - } - - @Override - public String getTag() { - return "-soyc"; - } - - @Override - public String[] getTagArgs() { - return new String[] {"/path/to/report/dir"}; - } - - @Override - public boolean setString(String str) { - jjsOptions.setSoycOutputDir(str); - return true; - } - } - - /** - * Argument handler for making the compiler run in "validation" mode. - */ - private class ArgHandlerValidateOnlyFlag extends ArgHandlerFlag { - - public String getPurpose() { - return "Validate all source code, but do not compile"; - } - - public String getTag() { - return "-validateOnly"; - } - - public boolean setFlag() { - jjsOptions.setValidateOnly(true); - return true; - } - } - - private class DistillerRebindPermutationOracle implements - RebindPermutationOracle { - - private StaticPropertyOracle[] propertyOracles; - private RebindOracle[] rebindOracles; - - public DistillerRebindPermutationOracle(ArtifactSet generatorArtifacts, - PropertyPermutations perms) { - propertyOracles = new StaticPropertyOracle[perms.size()]; - rebindOracles = new RebindOracle[perms.size()]; - BindingProperty[] orderedProps = perms.getOrderedProperties(); - SortedSet<ConfigurationProperty> configPropSet = module.getProperties().getConfigurationProperties(); - ConfigurationProperty[] configProps = configPropSet.toArray(new ConfigurationProperty[configPropSet.size()]); - for (int i = 0; i < rebindOracles.length; ++i) { - String[] orderedPropValues = perms.getOrderedPropertyValues(i); - propertyOracles[i] = new StaticPropertyOracle(orderedProps, - orderedPropValues, configProps); - rebindOracles[i] = new StandardRebindOracle(compilationState, - propertyOracles[i], module, rules, genDir, generatorResourcesDir, - generatorArtifacts); - } - } - - public String[] getAllPossibleRebindAnswers(TreeLogger logger, - String requestTypeName) throws UnableToCompleteException { - - String msg = "Computing all possible rebind results for '" - + requestTypeName + "'"; - logger = logger.branch(TreeLogger.DEBUG, msg, null); - - Set<String> answers = new HashSet<String>(); - - for (RebindOracle rebindOracle : rebindOracles) { - String resultTypeName = rebindOracle.rebind(logger, requestTypeName); - answers.add(resultTypeName); - } - return Util.toArray(String.class, answers); - } - - public int getPermuationCount() { - return rebindOracles.length; - } - - public StaticPropertyOracle getPropertyOracle(int permNumber) { - return propertyOracles[permNumber]; - } - - public RebindOracle getRebindOracle(int permNumber) { - return rebindOracles[permNumber]; - } - } - - public static final String GWT_COMPILER_DIR = ".gwt-tmp" + File.separatorChar - + "compiler"; - public static void main(String[] args) { /* * NOTE: main always exits with a call to System.exit to terminate any @@ -191,9 +44,14 @@ * shutdown AWT related threads, since the contract for their termination is * still implementation-dependent. */ - GWTCompiler compiler = new GWTCompiler(); - if (compiler.processArgs(args)) { - if (compiler.run()) { + final CompilerOptions options = new CompilerOptionsImpl(); + if (new ArgProcessor(options).processArgs(args)) { + CompileTask task = new CompileTask() { + public boolean run(TreeLogger logger) throws UnableToCompleteException { + return new GWTCompiler(options).run(logger); + } + }; + if (CompileTaskRunner.runWithAppropriateLogger(options, task)) { // Exit w/ success code. System.exit(0); } @@ -202,305 +60,34 @@ System.exit(1); } - private CompilationState compilationState; + private final CompilerOptionsImpl options; - private File genDir; - - private File generatorResourcesDir; - - private JJSOptions jjsOptions = new JJSOptions(); - - private Type logLevel; - - private ModuleDef module; - - private String moduleName; - - private File outDir; - - private Rules rules; - - private boolean useGuiLogger; - - public GWTCompiler() { - registerHandler(new ArgHandlerLogLevel() { - @Override - public void setLogLevel(Type level) { - logLevel = level; - } - }); - - registerHandler(new ArgHandlerGenDir() { - @Override - public void setDir(File dir) { - genDir = dir; - } - }); - - registerHandler(new ArgHandlerOutDir() { - @Override - public void setDir(File dir) { - outDir = dir; - } - }); - - registerHandler(new ArgHandlerTreeLoggerFlag() { - @Override - public boolean setFlag() { - useGuiLogger = true; - return true; - } - }); - - registerHandler(new ArgHandlerModuleName()); - - registerHandler(new ArgHandlerScriptStyle(jjsOptions)); - - registerHandler(new ArgHandlerStoryOfYourCompile()); - - registerHandler(new ArgHandlerEnableAssertions(jjsOptions)); - - registerHandler(new ArgHandlerDisableAggressiveOptimization() { - @Override - public boolean setFlag() { - GWTCompiler.this.setAggressivelyOptimize(false); - return true; - } - }); - - registerHandler(new ArgHandlerValidateOnlyFlag()); + public GWTCompiler(CompilerOptions options) { + this.options = new CompilerOptionsImpl(options); } - public void distill(TreeLogger logger, ModuleDef moduleDef) - throws UnableToCompleteException { - this.module = moduleDef; - this.compilationState = moduleDef.getCompilationState(); - - // Set up all the initial state. - checkModule(logger); - - // Place generated resources inside the out dir as a sibling to the module - generatorResourcesDir = new File(outDir, GWT_COMPILER_DIR + File.separator - + moduleDef.getName() + File.separator + "generated"); - - // Tweak the output directory so that output lives under the module name. - outDir = new File(outDir, module.getName()); - - // Clean the outDir. - Util.recursiveDelete(outDir, true); - - // Clean out the generated resources directory and/or create it. - Util.recursiveDelete(generatorResourcesDir, true); - generatorResourcesDir.mkdirs(); - - // TODO: All JDT checks now before even building TypeOracle? - compilationState.compile(logger); - - rules = module.getRules(); - String[] declEntryPts; - if (jjsOptions.isValidateOnly()) { - // TODO: revisit this.. do we even need to run JJS? - logger.log(TreeLogger.INFO, "Validating compilation " + module.getName(), - null); - // Pretend that every single compilation unit is an entry point. - Set<CompilationUnit> compilationUnits = compilationState.getCompilationUnits(); - declEntryPts = new String[compilationUnits.size()]; - int i = 0; - for (CompilationUnit unit : compilationUnits) { - declEntryPts[i++] = unit.getTypeName(); - } + public boolean run(TreeLogger logger) throws UnableToCompleteException { + if (options.isValidateOnly()) { + return new Precompile(options).run(logger); } else { - logger.log(TreeLogger.INFO, "Compiling module " + module.getName(), null); - // Use the real entry points. - declEntryPts = module.getEntryPointTypeNames(); - } - - ArtifactSet generatorArtifacts = new ArtifactSet(); - DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle( - generatorArtifacts, new PropertyPermutations(module.getProperties())); - - WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd( - compilationState, rpo); - JavaToJavaScriptCompiler jjs = new JavaToJavaScriptCompiler(logger, - frontEnd, declEntryPts, jjsOptions); - - if (jjsOptions.isValidateOnly()) { - logger.log(TreeLogger.INFO, "Validation succeeded", null); - return; - } - - StandardLinkerContext linkerContext = new StandardLinkerContext(logger, - module, outDir, generatorResourcesDir, jjsOptions); - compilePermutations(logger, jjs, rpo, linkerContext); - - logger.log(TreeLogger.INFO, "Compilation succeeded", null); - linkerContext.addOrReplaceArtifacts(generatorArtifacts); - linkerContext.link(logger, linkerContext, null); - } - - public File getGenDir() { - return genDir; - } - - public Type getLogLevel() { - return logLevel; - } - - public String getModuleName() { - return moduleName; - } - - public boolean getUseGuiLogger() { - return useGuiLogger; - } - - public void setAggressivelyOptimize(boolean aggressive) { - jjsOptions.setAggressivelyOptimize(aggressive); - } - - public void setCompilerOptions(JJSOptions options) { - jjsOptions.copyFrom(options); - } - - public void setGenDir(File dir) { - genDir = dir; - } - - public void setLogLevel(Type level) { - this.logLevel = level; - } - - public void setModuleName(String name) { - moduleName = name; - } - - public void setOutDir(File outDir) { - this.outDir = outDir; - } - - public void setStyleDetailed() { - jjsOptions.setOutput(JsOutputOption.DETAILED); - } - - public void setStyleObfuscated() { - jjsOptions.setOutput(JsOutputOption.OBFUSCATED); - } - - public void setStylePretty() { - jjsOptions.setOutput(JsOutputOption.PRETTY); - } - - /** - * Ensure the module has at least one entry point (except in validation mode). - */ - private void checkModule(TreeLogger logger) throws UnableToCompleteException { - if (!jjsOptions.isValidateOnly() - && module.getEntryPointTypeNames().length == 0) { - logger.log(TreeLogger.ERROR, "Module has no entry points defined", null); - throw new UnableToCompleteException(); - } - } - - private void compilePermutations(TreeLogger logger, - JavaToJavaScriptCompiler jjs, DistillerRebindPermutationOracle rpo, - StandardLinkerContext linkerContext) throws UnableToCompleteException { - - int permCount = rpo.getPermuationCount(); - PerfLogger.start("Compiling " + permCount + " permutations"); - Permutation[] perms = new Permutation[permCount]; - for (int i = 0; i < permCount; ++i) { - perms[i] = new Permutation(i, rpo.getRebindOracle(i), - rpo.getPropertyOracle(i)); - } - PermutationCompiler permCompiler = new PermutationCompiler(logger, jjs, - perms); - permCompiler.go(linkerContext); - } - - /** - * Runs the compiler. If a gui-based TreeLogger is used, this method will not - * return until its window is closed by the user. - * - * @return success from the compiler, <code>true</code> if the compile - * completed without errors, <code>false</code> otherwise. - */ - private boolean run() { - // Set any platform specific system properties. - BootStrapPlatform.applyPlatformHacks(); - - if (useGuiLogger) { - // Initialize a tree logger window. - DetachedTreeLoggerWindow loggerWindow = DetachedTreeLoggerWindow.getInstance( - "Build Output for " + moduleName, 800, 600, true); - - // Eager AWT initialization for OS X to ensure safe coexistence with SWT. - BootStrapPlatform.maybeInitializeAWT(); - - final AbstractTreeLogger logger = loggerWindow.getLogger(); - final boolean[] success = new boolean[1]; - - // Compiler will be spawned onto a second thread, UI thread for tree - // logger will remain on the main. - Thread compilerThread = new Thread(new Runnable() { - public void run() { - success[0] = GWTCompiler.this.run(logger); + logger = logger.branch(TreeLogger.INFO, "Compiling module " + + options.getModuleName()); + if (new Precompile(options).run(logger)) { + /* + * TODO: use the in-memory result of Precompile to run CompilePerms + * instead of serializing through the file system. + */ + CompilePermsOptionsImpl permsOptions = new CompilePermsOptionsImpl(); + permsOptions.copyFrom(options); + if (new CompilePerms(permsOptions).run(logger)) { + if (new Link(options).run(logger)) { + logger.log(TreeLogger.INFO, "Compilation succeeded"); + return true; + } } - }); - - compilerThread.setName("GWTCompiler Thread"); - compilerThread.start(); - loggerWindow.run(); - - // Even if the tree logger window is closed, we wait for the compiler - // to finish. - waitForThreadToTerminate(compilerThread); - - return success[0]; - } else { - return run(new PrintWriterTreeLogger()); - } - } - - private boolean run(AbstractTreeLogger logger) { - try { - logger.setMaxDetail(logLevel); - - ModuleDef moduleDef = ModuleDefLoader.loadFromClassPath(logger, - moduleName); - distill(logger, moduleDef); - return true; - } catch (UnableToCompleteException e) { - // We intentionally don't pass in the exception here since the real - // cause has been logged. - logger.log(TreeLogger.ERROR, "Build failed", null); + } + logger.log(TreeLogger.ERROR, "Compilation failed"); return false; } } - - /** - * Waits for a thread to terminate before it returns. This method is a - * non-cancellable task, in that it will defer thread interruption until it is - * done. - * - * @param godot the thread that is being waited on. - */ - private void waitForThreadToTerminate(final Thread godot) { - // Goetz pattern for non-cancellable tasks. - // http://www-128.ibm.com/developerworks/java/library/j-jtp05236.html - boolean isInterrupted = false; - try { - while (true) { - try { - godot.join(); - return; - } catch (InterruptedException e) { - isInterrupted = true; - } - } - } finally { - if (isInterrupted) { - Thread.currentThread().interrupt(); - } - } - } }
diff --git a/dev/core/src/com/google/gwt/dev/GWTShell.java b/dev/core/src/com/google/gwt/dev/GWTShell.java index 663a4e8..7a56d3d 100644 --- a/dev/core/src/com/google/gwt/dev/GWTShell.java +++ b/dev/core/src/com/google/gwt/dev/GWTShell.java
@@ -19,9 +19,9 @@ import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.TreeLogger.Type; import com.google.gwt.core.ext.typeinfo.TypeOracle; +import com.google.gwt.dev.Precompile.CompilerOptionsImpl; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; -import com.google.gwt.dev.jjs.JJSOptions; import com.google.gwt.dev.shell.BrowserWidget; import com.google.gwt.dev.shell.BrowserWidgetHost; import com.google.gwt.dev.shell.BrowserWidgetHostChecker; @@ -32,15 +32,15 @@ import com.google.gwt.dev.shell.ShellModuleSpaceHost; import com.google.gwt.dev.shell.tomcat.EmbeddedTomcatServer; import com.google.gwt.dev.util.PerfLogger; +import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization; +import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions; import com.google.gwt.dev.util.arg.ArgHandlerGenDir; import com.google.gwt.dev.util.arg.ArgHandlerLogLevel; +import com.google.gwt.dev.util.arg.ArgHandlerOutDir; import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle; import com.google.gwt.dev.util.log.AbstractTreeLogger; -import com.google.gwt.util.tools.ArgHandlerDisableAggressiveOptimization; -import com.google.gwt.util.tools.ArgHandlerEnableAssertions; import com.google.gwt.util.tools.ArgHandlerExtra; import com.google.gwt.util.tools.ArgHandlerFlag; -import com.google.gwt.util.tools.ArgHandlerOutDir; import com.google.gwt.util.tools.ArgHandlerString; import com.google.gwt.util.tools.ToolBase; @@ -246,12 +246,12 @@ // Create a sandbox for the module. // - File shellDir = new File(outDir, GWT_SHELL_PATH + File.separator - + moduleName); + File shellDir = new File(options.getOutDir(), GWT_SHELL_PATH + + File.separator + moduleName); TypeOracle typeOracle = moduleDef.getTypeOracle(logger); ShellModuleSpaceHost host = doCreateShellModuleSpaceHost(logger, - typeOracle, moduleDef, genDir, shellDir); + typeOracle, moduleDef, options.getGenDir(), shellDir); return host; } finally { Cursor normalCursor = display.getSystemCursor(SWT.CURSOR_ARROW); @@ -365,8 +365,6 @@ */ protected final Display display = Display.getDefault(); - protected File outDir; - /** * Cheat on the first load's refresh by assuming the module loaded by * {@link com.google.gwt.dev.shell.GWTShellServlet} is still fresh. This @@ -379,16 +377,12 @@ private final List<Shell> browserShells = new ArrayList<Shell>(); - private File genDir; - private boolean headlessMode = false; - private final JJSOptions jjsOptions = new JJSOptions(); - - private TreeLogger.Type logLevel; - private ShellMainWindow mainWnd; + private final CompilerOptionsImpl options = new CompilerOptionsImpl(); + private int port; private boolean runTomcat = true; @@ -411,47 +405,21 @@ registerHandler(new ArgHandlerWhitelist()); registerHandler(new ArgHandlerBlacklist()); - registerHandler(new ArgHandlerLogLevel() { - @Override - protected Type getDefaultLogLevel() { - return doGetDefaultLogLevel(); - } + registerHandler(new ArgHandlerLogLevel(options)); - @Override - public void setLogLevel(Type level) { - logLevel = level; - } - }); - - registerHandler(new ArgHandlerGenDir() { - @Override - public void setDir(File dir) { - genDir = dir; - } - }); + registerHandler(new ArgHandlerGenDir(options)); if (!noURLs) { registerHandler(new ArgHandlerStartupURLs()); } - registerHandler(new ArgHandlerOutDir() { - @Override - public void setDir(File dir) { - outDir = dir; - } - }); + registerHandler(new ArgHandlerOutDir(options)); - registerHandler(new ArgHandlerScriptStyle(jjsOptions)); + registerHandler(new ArgHandlerScriptStyle(options)); - registerHandler(new ArgHandlerEnableAssertions(jjsOptions)); + registerHandler(new ArgHandlerEnableAssertions(options)); - registerHandler(new ArgHandlerDisableAggressiveOptimization() { - @Override - public boolean setFlag() { - jjsOptions.setAggressivelyOptimize(false); - return true; - } - }); + registerHandler(new ArgHandlerDisableAggressiveOptimization(options)); } public void addStartupURL(String url) { @@ -464,16 +432,8 @@ } } - public File getGenDir() { - return genDir; - } - - public Type getLogLevel() { - return logLevel; - } - - public File getOutDir() { - return outDir; + public CompilerOptions getCompilerOptions() { + return new CompilerOptionsImpl(options); } public int getPort() { @@ -599,20 +559,8 @@ } } - public void setCompilerOptions(JJSOptions options) { - jjsOptions.copyFrom(options); - } - - public void setGenDir(File genDir) { - this.genDir = genDir; - } - - public void setLogLevel(Type level) { - this.logLevel = level; - } - - public void setOutDir(File outDir) { - this.outDir = outDir; + public void setCompilerOptions(CompilerOptions options) { + this.options.copyFrom(options); } public void setPort(int port) { @@ -630,13 +578,9 @@ */ protected void compile(TreeLogger logger, ModuleDef moduleDef) throws UnableToCompleteException { - GWTCompiler compiler = new GWTCompiler(); - compiler.setCompilerOptions(jjsOptions); - compiler.setGenDir(genDir); - compiler.setOutDir(outDir); - compiler.setModuleName(moduleDef.getName()); - compiler.setLogLevel(logLevel); - compiler.distill(logger, moduleDef); + CompilerOptions newOptions = new CompilerOptionsImpl(options); + newOptions.setModuleName(moduleDef.getName()); + new GWTCompiler(newOptions).run(logger); } /** @@ -685,7 +629,7 @@ protected void initializeLogger() { final AbstractTreeLogger logger = mainWnd.getLogger(); - logger.setMaxDetail(logLevel); + logger.setMaxDetail(options.getLogLevel()); } /** @@ -776,7 +720,7 @@ PerfLogger.start("GWTShell.startup (Tomcat launch)"); String whyFailed = EmbeddedTomcatServer.start(getTopLogger(), serverPort, - outDir); + options.getOutDir()); PerfLogger.end(); if (whyFailed != null) {
diff --git a/dev/core/src/com/google/gwt/dev/Link.java b/dev/core/src/com/google/gwt/dev/Link.java new file mode 100644 index 0000000..2bc55cd --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/Link.java
@@ -0,0 +1,215 @@ +/* + * 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.core.ext.linker.ArtifactSet; +import com.google.gwt.core.ext.linker.SelectionProperty; +import com.google.gwt.core.ext.linker.impl.StandardCompilationResult; +import com.google.gwt.core.ext.linker.impl.StandardLinkerContext; +import com.google.gwt.dev.CompileTaskRunner.CompileTask; +import com.google.gwt.dev.cfg.BindingProperty; +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.Util; +import com.google.gwt.dev.util.arg.ArgHandlerLogLevel; +import com.google.gwt.dev.util.arg.ArgHandlerModuleName; +import com.google.gwt.dev.util.arg.ArgHandlerOutDir; +import com.google.gwt.dev.util.arg.ArgHandlerTreeLoggerFlag; +import com.google.gwt.util.tools.ToolBase; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +/** + * Performs the first phase of compilation, generating the set of permutations + * to compile, and a ready-to-compile AST. + */ +public class Link { + + static class ArgProcessor extends ToolBase { + public ArgProcessor(CompileTaskOptions options) { + registerHandler(new ArgHandlerLogLevel(options)); + registerHandler(new ArgHandlerTreeLoggerFlag(options)); + registerHandler(new ArgHandlerOutDir(options)); + registerHandler(new ArgHandlerModuleName(options)); + } + + /* + * Overridden to make public. + */ + @Override + public boolean processArgs(String[] args) { + return super.processArgs(args); + } + + @Override + protected String getName() { + return Link.class.getName(); + } + } + + public static ArtifactSet link(TreeLogger logger, ModuleDef module, + Precompilation precompilation, File[] jsFiles) + throws UnableToCompleteException { + StandardLinkerContext linkerContext = new StandardLinkerContext(logger, + module, precompilation.getUnifiedAst().getOptions()); + return doLink(logger, linkerContext, precompilation, jsFiles); + } + + public static void main(String[] args) { + /* + * NOTE: main always exits with a call to System.exit to terminate any + * non-daemon threads that were started in Generators. Typically, this is to + * shutdown AWT related threads, since the contract for their termination is + * still implementation-dependent. + */ + final CompileTaskOptions options = new CompileTaskOptionsImpl(); + if (new ArgProcessor(options).processArgs(args)) { + CompileTask task = new CompileTask() { + public boolean run(TreeLogger logger) throws UnableToCompleteException { + return new Link(options).run(logger); + } + }; + if (CompileTaskRunner.runWithAppropriateLogger(options, task)) { + // Exit w/ success code. + System.exit(0); + } + } + // Exit w/ non-success code. + System.exit(1); + } + + private static ArtifactSet doLink(TreeLogger logger, + StandardLinkerContext linkerContext, Precompilation precompilation, + File[] jsFiles) throws UnableToCompleteException { + Permutation[] perms = precompilation.getPermutations(); + if (perms.length != jsFiles.length) { + throw new IllegalArgumentException( + "Mismatched jsFiles.length and permutation count"); + } + + for (int i = 0; i < perms.length; ++i) { + finishPermuation(logger, perms[i], jsFiles[i], linkerContext); + } + + linkerContext.addOrReplaceArtifacts(precompilation.getGeneratedArtifacts()); + return linkerContext.invokeLinkerStack(logger); + } + + private static void finishPermuation(TreeLogger logger, Permutation perm, + File jsFile, StandardLinkerContext linkerContext) + throws UnableToCompleteException { + StandardCompilationResult compilation = linkerContext.getCompilation( + logger, jsFile); + StaticPropertyOracle[] propOracles = perm.getPropertyOracles(); + for (StaticPropertyOracle propOracle : propOracles) { + BindingProperty[] orderedProps = propOracle.getOrderedProps(); + String[] orderedPropValues = propOracle.getOrderedPropValues(); + Map<SelectionProperty, String> unboundProperties = new HashMap<SelectionProperty, String>(); + for (int i = 0; i < orderedProps.length; i++) { + SelectionProperty key = linkerContext.getProperty(orderedProps[i].getName()); + if (key.tryGetValue() != null) { + /* + * The view of the Permutation doesn't include properties with defined + * values. + */ + continue; + } + unboundProperties.put(key, orderedPropValues[i]); + } + compilation.addSelectionPermutation(unboundProperties); + } + } + + private ModuleDef module; + + /** + * This is the output directory for private files. + */ + private File moduleAuxDir; + + /** + * This is the output directory for public files. + */ + private File moduleOutDir; + + private final CompileTaskOptionsImpl options; + + public Link(CompileTaskOptions options) { + this.options = new CompileTaskOptionsImpl(options); + } + + public boolean run(TreeLogger logger) throws UnableToCompleteException { + init(logger); + File precompilationFile = new File(options.getCompilerWorkDir(), + Precompile.PRECOMPILATION_FILENAME); + if (!precompilationFile.exists()) { + logger.log(TreeLogger.ERROR, "File not found '" + + precompilationFile.getAbsolutePath() + + "'; please run Precompile first"); + return false; + } + + Precompilation precompilation; + try { + precompilation = Util.readFileAsObject(precompilationFile, + Precompilation.class); + } catch (ClassNotFoundException e) { + logger.log(TreeLogger.ERROR, "Unable to deserialize '" + + precompilationFile.getAbsolutePath() + "'", e); + return false; + } + Permutation[] perms = precompilation.getPermutations(); + File[] jsFiles = new File[perms.length]; + for (int i = 0; i < perms.length; ++i) { + jsFiles[i] = CompilePerms.makePermFilename(options.getCompilerWorkDir(), + i); + if (!jsFiles[i].exists()) { + logger.log(TreeLogger.ERROR, "File not found '" + + precompilationFile.getAbsolutePath() + + "'; please compile all permutations"); + return false; + } + } + + TreeLogger branch = logger.branch(TreeLogger.INFO, "Linking module " + + module.getName()); + StandardLinkerContext linkerContext = new StandardLinkerContext(branch, + module, precompilation.getUnifiedAst().getOptions()); + ArtifactSet artifacts = doLink(branch, linkerContext, precompilation, + jsFiles); + if (artifacts != null) { + linkerContext.produceOutputDirectory(branch, artifacts, moduleOutDir, + moduleAuxDir); + branch.log(TreeLogger.INFO, "Link succeeded"); + return true; + } + branch.log(TreeLogger.ERROR, "Link failed"); + return false; + } + + private void init(TreeLogger logger) throws UnableToCompleteException { + module = ModuleDefLoader.loadFromClassPath(logger, options.getModuleName()); + moduleOutDir = new File(options.getOutDir(), module.getName()); + Util.recursiveDelete(moduleOutDir, true); + moduleAuxDir = new File(options.getOutDir(), module.getName() + "-aux"); + Util.recursiveDelete(moduleAuxDir, false); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/Permutation.java b/dev/core/src/com/google/gwt/dev/Permutation.java index 64ae9f3..13ca881 100644 --- a/dev/core/src/com/google/gwt/dev/Permutation.java +++ b/dev/core/src/com/google/gwt/dev/Permutation.java
@@ -16,34 +16,46 @@ package com.google.gwt.dev; import com.google.gwt.dev.cfg.StaticPropertyOracle; -import com.google.gwt.dev.jdt.RebindOracle; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; /** * Represents the state of a single permutation for compile. - * - * @see PermutationCompiler */ -public final class Permutation { - private final int number; - private final StaticPropertyOracle propertyOracle; - private final RebindOracle rebindOracle; +public final class Permutation implements Serializable { + private final List<StaticPropertyOracle> propertyOracles = new ArrayList<StaticPropertyOracle>(); + private final SortedMap<String, String> rebindAnswers = new TreeMap<String, String>(); - public Permutation(int number, RebindOracle rebindOracle, - StaticPropertyOracle propertyOracle) { - this.number = number; - this.rebindOracle = rebindOracle; - this.propertyOracle = propertyOracle; + public Permutation(StaticPropertyOracle propertyOracle) { + this.propertyOracles.add(propertyOracle); } - public int getNumber() { - return number; + public StaticPropertyOracle[] getPropertyOracles() { + return propertyOracles.toArray(new StaticPropertyOracle[propertyOracles.size()]); } - public StaticPropertyOracle getPropertyOracle() { - return propertyOracle; + public SortedMap<String, String> getRebindAnswers() { + return rebindAnswers; } - public RebindOracle getRebindOracle() { - return rebindOracle; + public void mergeFrom(Permutation other) { + assert rebindAnswers.equals(other.rebindAnswers); + assert !propertyOracles.isEmpty(); + assert !other.propertyOracles.isEmpty(); + propertyOracles.addAll(other.propertyOracles); + other.propertyOracles.clear(); + } + + public void putRebindAnswer(String requestType, String resultType) { + rebindAnswers.put(requestType, resultType); + } + + public void reduceRebindAnswers(Set<String> liveRebindRequests) { + rebindAnswers.keySet().retainAll(liveRebindRequests); } }
diff --git a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java index 7513e1c..f89b102 100644 --- a/dev/core/src/com/google/gwt/dev/PermutationCompiler.java +++ b/dev/core/src/com/google/gwt/dev/PermutationCompiler.java
@@ -17,17 +17,13 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.core.ext.linker.SelectionProperty; -import com.google.gwt.core.ext.linker.impl.StandardCompilationResult; -import com.google.gwt.core.ext.linker.impl.StandardLinkerContext; import com.google.gwt.dev.cfg.BindingProperty; import com.google.gwt.dev.cfg.StaticPropertyOracle; +import com.google.gwt.dev.jjs.UnifiedAst; import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler; import com.google.gwt.dev.jjs.ast.JProgram; import com.google.gwt.dev.util.PerfLogger; -import java.util.HashMap; -import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; @@ -40,13 +36,21 @@ 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, Throwable exception) { - super(perm); + public FailedResult(Permutation perm, int permNum, Throwable exception) { + super(perm, permNum); this.exception = exception; } @@ -60,42 +64,51 @@ */ private static final class PermutationTask implements Callable<String> { private static void logProperties(TreeLogger logger, - StaticPropertyOracle propOracle) { - 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); + 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 JavaToJavaScriptCompiler jjs; + private final UnifiedAst unifiedAst; private final TreeLogger logger; private final Permutation perm; + private final int permNum; - public PermutationTask(TreeLogger logger, JavaToJavaScriptCompiler jjs, - Permutation perm) { + public PermutationTask(TreeLogger logger, UnifiedAst unifiedAst, + Permutation perm, int permNum) { this.logger = logger; - this.jjs = jjs; + this.unifiedAst = unifiedAst; this.perm = perm; + this.permNum = permNum; } public String call() throws Exception { - PerfLogger.start("Permutation #" + (perm.getNumber() + 1)); + PerfLogger.start("Permutation #" + permNum); try { - TreeLogger branch = logger.branch(TreeLogger.TRACE, - "Analyzing permutation #" + (perm.getNumber() + 1)); - logProperties(branch, perm.getPropertyOracle()); - return jjs.compile(branch, perm.getRebindOracle()); + 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; } @@ -106,9 +119,15 @@ */ private abstract static class Result { private final Permutation perm; + private final int permNum; - public Result(Permutation perm) { + public Result(Permutation perm, int permNum) { this.perm = perm; + this.permNum = permNum; + } + + public int getPermNum() { + return permNum; } public Permutation getPermutation() { @@ -122,8 +141,8 @@ private static final class SuccessResult extends Result { private final String js; - public SuccessResult(Permutation perm, String js) { - super(perm); + public SuccessResult(Permutation perm, int permNum, String js) { + super(perm, permNum); this.js = js; } @@ -183,7 +202,8 @@ boolean definitelyFinalThread = (threadCount.get() == 1); try { String result = currentTask.call(); - results.add(new SuccessResult(currentTask.getPermutation(), result)); + 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. @@ -191,7 +211,7 @@ exitFinalThread(new Runnable() { public void run() { results.add(new FailedResult(currentTask.getPermutation(), - new UnableToCompleteException())); + currentTask.getPermNum(), new UnableToCompleteException())); } }); } @@ -211,7 +231,8 @@ outOfMemoryRetryAction.run(); } catch (Throwable e) { // Unexpected error compiling, this is unrecoverable. - results.add(new FailedResult(currentTask.getPermutation(), e)); + results.add(new FailedResult(currentTask.getPermutation(), + currentTask.getPermNum(), e)); throw new ThreadDeath(); } } @@ -270,7 +291,7 @@ /** * A marker Result that tells the main thread all work is done. */ - private static final Result FINISHED_RESULT = new Result(null) { + private static final Result FINISHED_RESULT = new Result(null, -1) { }; private static long getPotentialFreeMemory() { @@ -305,18 +326,17 @@ private final TreeLogger logger; - public PermutationCompiler(TreeLogger logger, JavaToJavaScriptCompiler jjs, - Permutation[] perms) { - this.logger = logger.branch(TreeLogger.DEBUG, "Compiling " + perms.length - + " permutations"); - this.astMemoryUsage = jjs.getAstMemoryUsage(); - for (Permutation perm : perms) { - tasks.add(new PermutationTask(this.logger, jjs, perm)); + public PermutationCompiler(TreeLogger logger, UnifiedAst unifiedAst, + Permutation[] perms, int[] permsToRun) { + this.logger = logger; + this.astMemoryUsage = unifiedAst.getAstMemoryUsage(); + for (int permToRun : permsToRun) { + tasks.add(new PermutationTask(logger, unifiedAst, perms[permToRun], + permToRun)); } } - public void go(StandardLinkerContext linkerContext) - throws UnableToCompleteException { + public void go(ResultsHandler handler) throws UnableToCompleteException { int initialThreadCount = computeInitialThreadCount(); Thread[] workerThreads = new Thread[initialThreadCount]; for (int i = 0; i < initialThreadCount; ++i) { @@ -333,7 +353,8 @@ assert threadCount.get() == 0; return; } else if (result instanceof SuccessResult) { - finishPermuation(logger, linkerContext, (SuccessResult) result); + 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()); @@ -415,30 +436,6 @@ return result; } - private void finishPermuation(TreeLogger logger, - StandardLinkerContext linkerContext, SuccessResult result) - throws UnableToCompleteException { - Permutation perm = result.getPermutation(); - StandardCompilationResult compilation = linkerContext.getCompilation( - logger, result.getJs()); - StaticPropertyOracle propOracle = perm.getPropertyOracle(); - BindingProperty[] orderedProps = propOracle.getOrderedProps(); - String[] orderedPropValues = propOracle.getOrderedPropValues(); - Map<SelectionProperty, String> unboundProperties = new HashMap<SelectionProperty, String>(); - for (int i = 0; i < orderedProps.length; i++) { - SelectionProperty key = linkerContext.getProperty(orderedProps[i].getName()); - if (key.tryGetValue() != null) { - /* - * The view of the Permutation doesn't include properties with defined - * values. - */ - continue; - } - unboundProperties.put(key, orderedPropValues[i]); - } - compilation.addSelectionPermutation(unboundProperties); - } - private UnableToCompleteException logAndTranslateException(Throwable e) { if (e instanceof UnableToCompleteException) { return (UnableToCompleteException) e;
diff --git a/dev/core/src/com/google/gwt/dev/Precompilation.java b/dev/core/src/com/google/gwt/dev/Precompilation.java new file mode 100644 index 0000000..8eb7438 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/Precompilation.java
@@ -0,0 +1,73 @@ +/* + * 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.linker.ArtifactSet; +import com.google.gwt.dev.jjs.UnifiedAst; + +import java.io.Serializable; + +/** + * The result of compilation phase 1, includes a unified AST and metadata + * relevant to each permutation. + */ +public class Precompilation implements Serializable { + /* + * TODO: don't make this whole class serializable, instead dump the + * independent members out to a file so that the generated artifacts are + * optional to deserialize. + */ + private ArtifactSet generatedArtifacts; + private final Permutation[] permutations; + private final UnifiedAst unifiedAst; + + /** + * Constructs a new precompilation. + * + * @param unifiedAst the unified AST used by + * {@link com.google.gwt.dev.jjs.JavaToJavaScriptCompiler} + * @param permutations the set of permutations that can be run + * @param generatedArtifacts the set of artifacts created by generators + */ + public Precompilation(UnifiedAst unifiedAst, Permutation[] permutations, + ArtifactSet generatedArtifacts) { + this.unifiedAst = unifiedAst; + this.permutations = permutations; + this.generatedArtifacts = generatedArtifacts; + } + + /** + * Returns the set of generated artifacts from the precompile phase. + */ + public ArtifactSet getGeneratedArtifacts() { + return generatedArtifacts; + } + + /** + * Returns the set of permutations to run. + */ + public Permutation[] getPermutations() { + return permutations; + } + + /** + * Returns the unified AST used by + * {@link com.google.gwt.dev.jjs.JavaToJavaScriptCompiler}. + */ + public UnifiedAst getUnifiedAst() { + return unifiedAst; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java new file mode 100644 index 0000000..8465ac5 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -0,0 +1,392 @@ +/* + * 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.core.ext.linker.ArtifactSet; +import com.google.gwt.dev.CompileTaskRunner.CompileTask; +import com.google.gwt.dev.cfg.BindingProperty; +import com.google.gwt.dev.cfg.ConfigurationProperty; +import com.google.gwt.dev.cfg.ModuleDef; +import com.google.gwt.dev.cfg.ModuleDefLoader; +import com.google.gwt.dev.cfg.PropertyPermutations; +import com.google.gwt.dev.cfg.Rules; +import com.google.gwt.dev.cfg.StaticPropertyOracle; +import com.google.gwt.dev.javac.CompilationUnit; +import com.google.gwt.dev.jdt.RebindOracle; +import com.google.gwt.dev.jdt.RebindPermutationOracle; +import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd; +import com.google.gwt.dev.jjs.UnifiedAst; +import com.google.gwt.dev.jjs.JJSOptions; +import com.google.gwt.dev.jjs.JJSOptionsImpl; +import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler; +import com.google.gwt.dev.jjs.JsOutputOption; +import com.google.gwt.dev.shell.StandardRebindOracle; +import com.google.gwt.dev.util.Util; +import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization; +import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions; +import com.google.gwt.dev.util.arg.ArgHandlerGenDir; +import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle; +import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag; + +import java.io.File; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; +import java.util.TreeMap; + +/** + * Performs the first phase of compilation, generating the set of permutations + * to compile, and a ready-to-compile AST. + */ +public class Precompile { + + static class ArgProcessor extends Link.ArgProcessor { + public ArgProcessor(CompilerOptions options) { + super(options); + registerHandler(new ArgHandlerGenDir(options)); + registerHandler(new ArgHandlerScriptStyle(options)); + registerHandler(new ArgHandlerEnableAssertions(options)); + registerHandler(new ArgHandlerDisableAggressiveOptimization(options)); + registerHandler(new ArgHandlerValidateOnlyFlag(options)); + } + + @Override + protected String getName() { + return Precompile.class.getName(); + } + } + /** + * Concrete class to implement all compiler options. + */ + static class CompilerOptionsImpl extends CompileTaskOptionsImpl implements + CompilerOptions { + + private File genDir; + private final JJSOptionsImpl jjsOptions = new JJSOptionsImpl(); + private boolean validateOnly; + + public CompilerOptionsImpl() { + } + + public CompilerOptionsImpl(CompilerOptions other) { + copyFrom(other); + } + + public void copyFrom(CompilerOptions other) { + super.copyFrom(other); + + setGenDir(other.getGenDir()); + setValidateOnly(other.isValidateOnly()); + + setAggressivelyOptimize(other.isAggressivelyOptimize()); + setEnableAssertions(other.isEnableAssertions()); + setOutput(other.getOutput()); + } + + public File getGenDir() { + return genDir; + } + + public JsOutputOption getOutput() { + return jjsOptions.getOutput(); + } + + public boolean isAggressivelyOptimize() { + return jjsOptions.isAggressivelyOptimize(); + } + + public boolean isEnableAssertions() { + return jjsOptions.isEnableAssertions(); + } + + public boolean isValidateOnly() { + return validateOnly; + } + + public void setAggressivelyOptimize(boolean aggressivelyOptimize) { + jjsOptions.setAggressivelyOptimize(aggressivelyOptimize); + } + + public void setEnableAssertions(boolean enableAssertions) { + jjsOptions.setEnableAssertions(enableAssertions); + } + + public void setGenDir(File genDir) { + this.genDir = genDir; + } + + public void setOutput(JsOutputOption output) { + jjsOptions.setOutput(output); + } + + public void setValidateOnly(boolean validateOnly) { + this.validateOnly = validateOnly; + } + } + + private static class DistillerRebindPermutationOracle implements + RebindPermutationOracle { + + private Permutation[] permutations; + private StaticPropertyOracle[] propertyOracles; + private RebindOracle[] rebindOracles; + + public DistillerRebindPermutationOracle(ModuleDef module, + ArtifactSet generatorArtifacts, PropertyPermutations perms, + File genDir, File generatorResourcesDir) { + permutations = new Permutation[perms.size()]; + propertyOracles = new StaticPropertyOracle[perms.size()]; + rebindOracles = new RebindOracle[perms.size()]; + BindingProperty[] orderedProps = perms.getOrderedProperties(); + SortedSet<ConfigurationProperty> configPropSet = module.getProperties().getConfigurationProperties(); + ConfigurationProperty[] configProps = configPropSet.toArray(new ConfigurationProperty[configPropSet.size()]); + Rules rules = module.getRules(); + for (int i = 0; i < rebindOracles.length; ++i) { + String[] orderedPropValues = perms.getOrderedPropertyValues(i); + propertyOracles[i] = new StaticPropertyOracle(orderedProps, + orderedPropValues, configProps); + rebindOracles[i] = new StandardRebindOracle( + module.getCompilationState(), propertyOracles[i], module, rules, + genDir, generatorResourcesDir, generatorArtifacts); + permutations[i] = new Permutation(propertyOracles[i]); + } + } + + public String[] getAllPossibleRebindAnswers(TreeLogger logger, + String requestTypeName) throws UnableToCompleteException { + + String msg = "Computing all possible rebind results for '" + + requestTypeName + "'"; + logger = logger.branch(TreeLogger.DEBUG, msg, null); + + Set<String> answers = new HashSet<String>(); + + for (int i = 0; i < getPermuationCount(); ++i) { + String resultTypeName = rebindOracles[i].rebind(logger, requestTypeName); + answers.add(resultTypeName); + // Record the correct answer into each permutation. + permutations[i].putRebindAnswer(requestTypeName, resultTypeName); + } + return Util.toArray(String.class, answers); + } + + public int getPermuationCount() { + return rebindOracles.length; + } + + public Permutation[] getPermutations() { + return permutations; + } + + public StaticPropertyOracle getPropertyOracle(int permNumber) { + return propertyOracles[permNumber]; + } + + public RebindOracle getRebindOracle(int permNumber) { + return rebindOracles[permNumber]; + } + } + + static final String PERM_COUNT_FILENAME = "permCount.txt"; + + static final String PRECOMPILATION_FILENAME = "precompilation.ser"; + + /** + * Performs a command-line precompile. + */ + public static void main(String[] args) { + /* + * NOTE: main always exits with a call to System.exit to terminate any + * non-daemon threads that were started in Generators. Typically, this is to + * shutdown AWT related threads, since the contract for their termination is + * still implementation-dependent. + */ + final CompilerOptions options = new CompilerOptionsImpl(); + if (new ArgProcessor(options).processArgs(args)) { + CompileTask task = new CompileTask() { + public boolean run(TreeLogger logger) throws UnableToCompleteException { + return new Precompile(options).run(logger); + } + }; + if (CompileTaskRunner.runWithAppropriateLogger(options, task)) { + // Exit w/ success code. + System.exit(0); + } + } + // Exit w/ non-success code. + System.exit(1); + } + + /** + * Precompiles the given module. + * + * @param logger a logger to use + * @param jjsOptions a set of compiler options + * @param module the module to compile + * @param genDir optional directory to dump generated source, may be + * <code>null</code> + * @param generatorResourcesDir required directory to dump generator resources + * @return the precompilation + */ + public static Precompilation precompile(TreeLogger logger, + JJSOptions jjsOptions, ModuleDef module, File genDir, + File generatorResourcesDir) { + try { + String[] declEntryPts = module.getEntryPointTypeNames(); + if (declEntryPts.length == 0) { + logger.log(TreeLogger.ERROR, "Module has no entry points defined", null); + throw new UnableToCompleteException(); + } + + ArtifactSet generatedArtifacts = new ArtifactSet(); + DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle( + module, generatedArtifacts, new PropertyPermutations( + module.getProperties()), genDir, generatorResourcesDir); + + WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd( + module.getCompilationState(), rpo); + UnifiedAst unifiedAst = JavaToJavaScriptCompiler.precompile(logger, + frontEnd, declEntryPts, jjsOptions, rpo.getPermuationCount() == 1); + + // Merge all identical permutations together. + Permutation[] permutations = rpo.getPermutations(); + // Sort the permutations by an ordered key to ensure determinism. + SortedMap<String, Permutation> merged = new TreeMap<String, Permutation>(); + for (Permutation permutation : permutations) { + permutation.reduceRebindAnswers(unifiedAst.getRebindRequests()); + // Arbitrarily choose as a key the stringified map of rebind answers. + String rebindResultsString = permutation.getRebindAnswers().toString(); + if (merged.containsKey(rebindResultsString)) { + Permutation existing = merged.get(rebindResultsString); + existing.mergeFrom(permutation); + } else { + merged.put(rebindResultsString, permutation); + } + } + permutations = merged.values().toArray(new Permutation[merged.size()]); + return new Precompilation(unifiedAst, permutations, generatedArtifacts); + } catch (UnableToCompleteException e) { + // We intentionally don't pass in the exception here since the real + // cause has been logged. + return null; + } + } + + /** + * Validates the given module can be compiled. + * + * @param logger a logger to use + * @param jjsOptions a set of compiler options + * @param module the module to compile + * @param genDir optional directory to dump generated source, may be + * <code>null</code> + * @param generatorResourcesDir required directory to dump generator resources + */ + public static boolean validate(TreeLogger logger, JJSOptions jjsOptions, + ModuleDef module, File genDir, File generatorResourcesDir) { + try { + String[] declEntryPts = module.getEntryPointTypeNames(); + if (declEntryPts.length == 0) { + // Pretend that every single compilation unit is an entry point. + Set<CompilationUnit> compilationUnits = module.getCompilationState().getCompilationUnits(); + declEntryPts = new String[compilationUnits.size()]; + int i = 0; + for (CompilationUnit unit : compilationUnits) { + declEntryPts[i++] = unit.getTypeName(); + } + } + + ArtifactSet generatorArtifacts = new ArtifactSet(); + DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle( + module, generatorArtifacts, new PropertyPermutations( + module.getProperties()), genDir, generatorResourcesDir); + + WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd( + module.getCompilationState(), rpo); + JavaToJavaScriptCompiler.precompile(logger, frontEnd, declEntryPts, + jjsOptions, true); + return true; + } catch (UnableToCompleteException e) { + // Already logged. + return false; + } + } + + private File generatorResourcesDir; + + private ModuleDef module; + + private final CompilerOptionsImpl options; + + public Precompile(CompilerOptions options) { + this.options = new CompilerOptionsImpl(options); + } + + public boolean run(TreeLogger logger) throws UnableToCompleteException { + if (options.isValidateOnly()) { + init(logger); + TreeLogger branch = logger.branch(TreeLogger.INFO, + "Validating compilation " + module.getName()); + if (validate(branch, options, module, options.getGenDir(), + generatorResourcesDir)) { + branch.log(TreeLogger.INFO, "Validation succeeded"); + return true; + } else { + branch.log(TreeLogger.ERROR, "Validation failed"); + return false; + } + } else { + init(logger); + TreeLogger branch = logger.branch(TreeLogger.INFO, "Precompiling module " + + module.getName()); + Precompilation precompilation = precompile(branch, options, module, + options.getGenDir(), generatorResourcesDir); + if (precompilation != null) { + Util.writeObjectAsFile(branch, new File(options.getCompilerWorkDir(), + PRECOMPILATION_FILENAME), precompilation); + Util.writeStringAsFile(branch, new File(options.getCompilerWorkDir(), + PERM_COUNT_FILENAME), + String.valueOf(precompilation.getPermutations().length)); + branch.log(TreeLogger.INFO, + "Precompilation succeeded, number of permutations: " + + precompilation.getPermutations().length); + return true; + } + branch.log(TreeLogger.ERROR, "Precompilation failed"); + return false; + } + } + + private void init(TreeLogger logger) throws UnableToCompleteException { + // Clean out the work dir and/or create it. + File compilerWorkDir = options.getCompilerWorkDir(); + Util.recursiveDelete(compilerWorkDir, true); + compilerWorkDir.mkdirs(); + + this.module = ModuleDefLoader.loadFromClassPath(logger, + options.getModuleName()); + + // Place generated resources inside the work dir. + generatorResourcesDir = new File(compilerWorkDir, "generated"); + generatorResourcesDir.mkdirs(); + + // TODO: All JDT checks now before even building TypeOracle? + module.getCompilationState().compile(logger); + } +}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java b/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java deleted file mode 100644 index ddb7c7b..0000000 --- a/dev/core/src/com/google/gwt/dev/cfg/DefaultPropertyProvider.java +++ /dev/null
@@ -1,70 +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.cfg; - -import com.google.gwt.dev.js.JsParser; -import com.google.gwt.dev.js.JsParserException; -import com.google.gwt.dev.js.ast.JsBlock; -import com.google.gwt.dev.js.ast.JsExprStmt; -import com.google.gwt.dev.js.ast.JsFunction; -import com.google.gwt.dev.js.ast.JsProgram; -import com.google.gwt.dev.js.ast.JsStatement; - -import java.io.IOException; -import java.io.StringReader; -import java.util.List; - -/** - * A property provider that reports property values specified literally in a - * host HTML page. - */ -public class DefaultPropertyProvider extends PropertyProvider { - - /* - * TODO: this references 'parent' literally, which could be a problem if you - * were to include the selector script in the host page itself rather than in - * an iframe. - */ - public DefaultPropertyProvider(ModuleDef module, Property property) { - super(module, property); - String src = "function () {"; - src += "return __gwt_getMetaProperty(\""; - src += property.getName(); - src += "\"); }"; - setBody(parseFunction(src)); - } - - private JsBlock parseFunction(String jsniSrc) { - Throwable caught = null; - try { - JsProgram jsPgm = new JsProgram(); - JsParser jsParser = new JsParser(); - StringReader r = new StringReader(jsniSrc); - jsParser.setSourceInfo(jsPgm.createSourceInfoSynthetic( - DefaultPropertyProvider.class, "Default property provider for " - + getProperty().getName())); - List<JsStatement> stmts = jsParser.parse(jsPgm.getScope(), r, 1); - JsFunction fn = (JsFunction) ((JsExprStmt) stmts.get(0)).getExpression(); - return fn.getBody(); - } catch (IOException e) { - caught = e; - } catch (JsParserException e) { - caught = e; - } - throw new RuntimeException( - "Internal error parsing source for default property provider", caught); - } -}
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java index 405e93c..3d6efb6 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java +++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDef.java
@@ -408,7 +408,11 @@ * one possible value and no existing provider. */ if (prop.getProvider() == null && prop.getAllowedValues().length > 1) { - prop.setProvider(new DefaultPropertyProvider(this, prop)); + String src = "{"; + src += "return __gwt_getMetaProperty(\""; + src += prop.getName(); + src += "\"); }"; + prop.setProvider(new PropertyProvider(src)); } } }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java index 2477a25..ebde69d 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java +++ b/dev/core/src/com/google/gwt/dev/cfg/ModuleDefSchema.java
@@ -214,7 +214,6 @@ } protected Schema __property_provider_begin(BindingProperty property) { - property.setProvider(new PropertyProvider(moduleDef, property)); return fChild = new PropertyProviderBodySchema(); } @@ -233,7 +232,7 @@ int lineNumber = childSchema.getStartLineNumber(); JsFunction fn = parseJsBlock(lineNumber, script); - property.getProvider().setBody(fn.getBody()); + property.setProvider(new PropertyProvider(fn.getBody().toSource())); } protected Schema __public_begin(String path, String includes,
diff --git a/dev/core/src/com/google/gwt/dev/cfg/Property.java b/dev/core/src/com/google/gwt/dev/cfg/Property.java index ad30a8b..5615672 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/Property.java +++ b/dev/core/src/com/google/gwt/dev/cfg/Property.java
@@ -15,10 +15,12 @@ */ package com.google.gwt.dev.cfg; +import java.io.Serializable; + /** * Represents an abstract module property. */ -public abstract class Property implements Comparable<Property> { +public abstract class Property implements Comparable<Property>, Serializable { protected final String name;
diff --git a/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java b/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java index 13c44d6..55f5f17 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java +++ b/dev/core/src/com/google/gwt/dev/cfg/PropertyProvider.java
@@ -1,5 +1,5 @@ /* - * Copyright 2006 Google Inc. + * 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 @@ -15,36 +15,20 @@ */ package com.google.gwt.dev.cfg; -import com.google.gwt.dev.js.ast.JsBlock; +import java.io.Serializable; /** * Produces a deferred binding property value by executing JavaScript code. */ -public class PropertyProvider { +public class PropertyProvider implements Serializable { - private JsBlock body; + private final String body; - private final ModuleDef module; - private final Property property; - - public PropertyProvider(ModuleDef module, Property property) { - this.module = module; - this.property = property; - } - - public JsBlock getBody() { - return body; - } - - public ModuleDef getModule() { - return module; - } - - public Property getProperty() { - return property; - } - - public void setBody(JsBlock body) { + public PropertyProvider(String body) { this.body = body; } + + public String getBody() { + return body; + } }
diff --git a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java index f501fd2..d0cb379 100644 --- a/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java +++ b/dev/core/src/com/google/gwt/dev/cfg/StaticPropertyOracle.java
@@ -19,11 +19,13 @@ import com.google.gwt.core.ext.PropertyOracle; import com.google.gwt.core.ext.TreeLogger; +import java.io.Serializable; + /** * An implementation of {@link PropertyOracle} that contains property values, * rather than computing them. */ -public class StaticPropertyOracle implements PropertyOracle { +public class StaticPropertyOracle implements PropertyOracle, Serializable { private final ConfigurationProperty[] configProps;
diff --git a/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java b/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java index 8108e76..a8b010c 100644 --- a/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java +++ b/dev/core/src/com/google/gwt/dev/jdt/RebindPermutationOracle.java
@@ -29,6 +29,4 @@ */ String[] getAllPossibleRebindAnswers(TreeLogger logger, String sourceTypeName) throws UnableToCompleteException; - - int getPermuationCount(); }
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 c80e722..6e9e162 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptions.java
@@ -15,100 +15,13 @@ */ package com.google.gwt.dev.jjs; +import com.google.gwt.dev.util.arg.OptionAggressivelyOptimize; +import com.google.gwt.dev.util.arg.OptionEnableAssertions; +import com.google.gwt.dev.util.arg.OptionScriptStyle; + /** * Controls options for the {@link JavaToJavaScriptCompiler}. */ -public class JJSOptions { - - private boolean aggressivelyOptimize = true; - private boolean enableAssertions = false; - private JsOutputOption output = JsOutputOption.OBFUSCATED; - private String soycOutputPath = null; - private boolean validateOnly = false; - - public JJSOptions() { - } - - public JJSOptions(JJSOptions other) { - copyFrom(other); - } - - public void copyFrom(JJSOptions other) { - this.aggressivelyOptimize = other.aggressivelyOptimize; - this.enableAssertions = other.enableAssertions; - this.soycOutputPath = other.soycOutputPath; - this.output = other.output; - this.validateOnly = other.validateOnly; - } - - /** - * Returns the output format setting. - */ - public JsOutputOption getOutput() { - return output; - } - - /** - * Returns the path of the SOYC output directory, if it has been set. - */ - public String getSoycOutputDir() { - return soycOutputPath; - } - - /** - * Returns true if the compiler should aggressively optimize. - */ - public boolean isAggressivelyOptimize() { - return aggressivelyOptimize; - } - - /** - * Returns true if the compiler should generate code to check assertions. - */ - public boolean isEnableAssertions() { - return enableAssertions; - } - - /** - * /** Returns true if the compiler should run in validation mode, not - * producing any output. - */ - public boolean isValidateOnly() { - return validateOnly; - } - - /** - * Sets whether or not the compiler should aggressively optimize. - */ - public void setAggressivelyOptimize(boolean aggressivelyOptimize) { - this.aggressivelyOptimize = aggressivelyOptimize; - } - - /** - * Sets whether or not the compiler should generate code to check assertions. - */ - public void setEnableAssertions(boolean enableAssertions) { - this.enableAssertions = enableAssertions; - } - - /** - * Sets the compiler output option. - */ - public void setOutput(JsOutputOption output) { - this.output = output; - } - - /** - * Sets the output path of the SOYC reports. - */ - public void setSoycOutputDir(String path) { - this.soycOutputPath = path; - } - - /** - * Sets whether or not the compiler should run in validation mode. - */ - public void setValidateOnly(boolean validateOnly) { - this.validateOnly = validateOnly; - } +public interface JJSOptions extends OptionAggressivelyOptimize, + OptionEnableAssertions, OptionScriptStyle { }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java new file mode 100644 index 0000000..8873c41 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/jjs/JJSOptionsImpl.java
@@ -0,0 +1,65 @@ +/* + * 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 java.io.Serializable; + +/** + * Concrete class to implement all JJS options. + */ +public class JJSOptionsImpl implements JJSOptions, Serializable { + + private boolean aggressivelyOptimize = true; + private boolean enableAssertions; + private JsOutputOption output = JsOutputOption.OBFUSCATED; + + public JJSOptionsImpl() { + } + + public JJSOptionsImpl(JJSOptions other) { + copyFrom(other); + } + + public void copyFrom(JJSOptions other) { + setAggressivelyOptimize(other.isAggressivelyOptimize()); + setEnableAssertions(other.isEnableAssertions()); + setOutput(other.getOutput()); + } + + public JsOutputOption getOutput() { + return output; + } + + public boolean isAggressivelyOptimize() { + return aggressivelyOptimize; + } + + public boolean isEnableAssertions() { + return enableAssertions; + } + + public void setAggressivelyOptimize(boolean aggressivelyOptimize) { + this.aggressivelyOptimize = aggressivelyOptimize; + } + + public void setEnableAssertions(boolean enableAssertions) { + this.enableAssertions = enableAssertions; + } + + public void setOutput(JsOutputOption output) { + this.output = output; + } +}
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 edf1d27..10ef174 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -17,10 +17,10 @@ import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.dev.jdt.RebindOracle; import com.google.gwt.dev.jdt.RebindPermutationOracle; import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd; import com.google.gwt.dev.jjs.InternalCompilerException.NodeInfo; +import com.google.gwt.dev.jjs.UnifiedAst.AST; import com.google.gwt.dev.jjs.ast.JBinaryOperation; import com.google.gwt.dev.jjs.ast.JBinaryOperator; import com.google.gwt.dev.jjs.ast.JClassType; @@ -54,6 +54,7 @@ import com.google.gwt.dev.jjs.impl.MethodInliner; import com.google.gwt.dev.jjs.impl.PostOptimizationCompoundAssignmentNormalizer; import com.google.gwt.dev.jjs.impl.Pruner; +import com.google.gwt.dev.jjs.impl.RecordRebinds; import com.google.gwt.dev.jjs.impl.ReplaceRebinds; import com.google.gwt.dev.jjs.impl.ResolveRebinds; import com.google.gwt.dev.jjs.impl.TypeMap; @@ -69,7 +70,6 @@ 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.SourceInfoHistogram; import com.google.gwt.dev.js.ast.JsProgram; import com.google.gwt.dev.util.DefaultTextOutput; import com.google.gwt.dev.util.PerfLogger; @@ -80,23 +80,362 @@ import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; 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 { + /** + * Compiles a particular permutation, based on a precompiled unified AST. + * + * @param logger the logger to use + * @param unifiedAst the result of a + * {@link #precompile(TreeLogger, WebModeCompilerFrontEnd, String[], JJSOptions, boolean)} + * @param rebindAnswers the set of rebind answers to resolve all outstanding + * rebind decisions + * @return the output JavaScript + * @throws UnableToCompleteException if an error other than + * {@link OutOfMemoryError} occurs + */ + public static String compilePermutation(TreeLogger logger, + UnifiedAst unifiedAst, Map<String, String> rebindAnswers) + throws UnableToCompleteException { + try { + if (JProgram.isTracingEnabled()) { + System.out.println("------------------------------------------------------------"); + System.out.println("| (new permuation) |"); + System.out.println("------------------------------------------------------------"); + } + + AST ast = unifiedAst.getFreshAst(); + JProgram jprogram = ast.getJProgram(); + JsProgram jsProgram = ast.getJsProgram(); + JJSOptions options = unifiedAst.getOptions(); + + ResolveRebinds.exec(jprogram, rebindAnswers); + + // (4) Optimize the normalized Java AST for each permutation. + optimize(options, jprogram); + + // (5) "Normalize" the high-level Java tree into a lower-level tree more + // suited for JavaScript code generation. Don't go reordering these + // willy-nilly because there are some subtle interdependencies. + LongCastNormalizer.exec(jprogram); + JsoDevirtualizer.exec(jprogram); + CatchBlockNormalizer.exec(jprogram); + PostOptimizationCompoundAssignmentNormalizer.exec(jprogram); + LongEmulationNormalizer.exec(jprogram); + CastNormalizer.exec(jprogram); + ArrayNormalizer.exec(jprogram); + EqualityNormalizer.exec(jprogram); + + // (6) Perform further post-normalization optimizations + // Prune everything + Pruner.exec(jprogram, false); + + // (7) Generate a JavaScript code DOM from the Java type declarations + jprogram.typeOracle.recomputeClinits(); + GenerateJavaScriptAST.exec(jprogram, jsProgram, options.getOutput()); + + // Allow GC. + jprogram = null; + + // (8) Normalize the JS AST. + // Fix invalid constructs created during JS AST gen. + JsNormalizer.exec(jsProgram); + // Resolve all unresolved JsNameRefs. + JsSymbolResolver.exec(jsProgram); + + // (9) Optimize the JS AST. + if (options.isAggressivelyOptimize()) { + boolean didChange; + do { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + + didChange = false; + // Remove unused functions, possible + didChange = JsStaticEval.exec(jsProgram) || didChange; + // Inline JavaScript function invocations + didChange = JsInliner.exec(jsProgram) || didChange; + // Remove unused functions, possible + didChange = JsUnusedFunctionRemover.exec(jsProgram) || didChange; + } while (didChange); + } + + // (10) Obfuscate + switch (options.getOutput()) { + case OBFUSCATED: + JsStringInterner.exec(jsProgram); + JsObfuscateNamer.exec(jsProgram); + break; + case PRETTY: + // We don't intern strings in pretty mode to improve readability + JsPrettyNamer.exec(jsProgram); + break; + case DETAILED: + JsStringInterner.exec(jsProgram); + JsVerboseNamer.exec(jsProgram); + break; + default: + throw new InternalCompilerException("Unknown output mode"); + } + + // (11) Perform any post-obfuscation normalizations. + + // Work around an IE7 bug, + // http://code.google.com/p/google-web-toolkit/issues/detail?id=1440 + JsIEBlockSizeVisitor.exec(jsProgram); + + // (12) Generate the final output text. + DefaultTextOutput out = new DefaultTextOutput( + options.getOutput().shouldMinimize()); + JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); + v.accept(jsProgram); + return out.toString(); + } catch (Throwable e) { + throw logAndTranslateException(logger, e); + } + } + + /** + * Performs a precompilation, returning a unified AST. + * + * @param logger the logger to use + * @param compilerFrontEnd the compiler front ent + * @param declEntryPts the set of entry classes + * @param options the compiler options + * @param singlePermutation if true, do not pre-optimize the resulting AST or + * allow serialization of the result + * @return the unified AST used to drive permutation compiles + * @throws UnableToCompleteException if an error other than + * {@link OutOfMemoryError} occurs + */ + public static UnifiedAst precompile(TreeLogger logger, + WebModeCompilerFrontEnd compilerFrontEnd, String[] declEntryPts, + JJSOptions options, boolean singlePermutation) + throws UnableToCompleteException { + + if (declEntryPts.length == 0) { + throw new IllegalArgumentException("entry point(s) required"); + } + + RebindPermutationOracle rpo = compilerFrontEnd.getRebindPermutationOracle(); + + // Find all the possible rebound entry points. + Set<String> allEntryPoints = new TreeSet<String>(); + for (String element : declEntryPts) { + String[] all = rpo.getAllPossibleRebindAnswers(logger, element); + Util.addAll(allEntryPoints, all); + } + allEntryPoints.addAll(JProgram.CODEGEN_TYPES_SET); + allEntryPoints.addAll(JProgram.INDEX_TYPES_SET); + + // Compile the source and get the compiler so we can get the parse tree + // + CompilationUnitDeclaration[] goldenCuds = compilerFrontEnd.getCompilationUnitDeclarations( + logger, allEntryPoints.toArray(new String[0])); + + // Check for compilation problems. We don't log here because any problems + // found here will have already been logged by AbstractCompiler. + // + checkForErrors(logger, goldenCuds, false); + + PerfLogger.start("Build AST"); + JProgram jprogram = new JProgram(); + JsProgram jsProgram = new JsProgram(); + + try { + long usedMemoryBefore = singlePermutation ? 0 : getUsedMemory(); + + /* + * (1) Build a flattened map of TypeDeclarations => JType. The resulting + * map contains entries for all reference types. BuildTypeMap also parses + * all JSNI. + */ + TypeMap typeMap = new TypeMap(jprogram); + TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap, + goldenCuds, jsProgram); + + // BuildTypeMap can uncover syntactic JSNI errors; report & abort + checkForErrors(logger, goldenCuds, true); + + // Compute all super type/sub type info + jprogram.typeOracle.computeBeforeAST(); + + // (2) Create our own Java AST from the JDT AST. + GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram, + options.isEnableAssertions()); + + long usedMemoryAfter = singlePermutation ? 0 : getUsedMemory(); + long memoryDelta = usedMemoryAfter - usedMemoryBefore; + long astMemoryUsage = (long) (memoryDelta * 1.5); + + // GenerateJavaAST can uncover semantic JSNI errors; report & abort + checkForErrors(logger, goldenCuds, true); + + // Allow GC + goldenCuds = null; + typeMap = null; + allTypeDeclarations = null; + + // (3) Perform Java AST normalizations. + + FixAssignmentToUnbox.exec(jprogram); + + /* + * TODO: If we defer this until later, we could maybe use the results of + * the assertions to enable more optimizations. + */ + if (options.isEnableAssertions()) { + // Turn into assertion checking calls. + AssertionNormalizer.exec(jprogram); + } else { + // Remove all assert statements. + AssertionRemover.exec(jprogram); + } + + // Replace GWT.create calls with JGwtCreate nodes. + ReplaceRebinds.exec(logger, jprogram, rpo); + + // Resolve entry points, rebinding non-static entry points. + findEntryPoints(logger, rpo, declEntryPts, jprogram); + + // Replace references to JSO subtypes with JSO itself. + JavaScriptObjectNormalizer.exec(jprogram); + + /* + * (4) Optimize the normalized Java AST for the common AST. By doing + * optimizations early in the multiple permutation scenario, we're saving + * work. However, we can't fully optimize because we don't yet know the + * deferred binding decisions. + * + * Don't bother optimizing early if there's only one permutation. + */ + if (!singlePermutation) { + optimize(options, jprogram); + } + + Set<String> rebindRequests = new HashSet<String>(); + RecordRebinds.exec(jprogram, rebindRequests); + + return new UnifiedAst(options, new AST(jprogram, jsProgram), + singlePermutation, astMemoryUsage, rebindRequests); + } catch (Throwable e) { + throw logAndTranslateException(logger, e); + } finally { + PerfLogger.end(); + } + } + + protected static void optimize(JJSOptions options, JProgram jprogram) + throws InterruptedException { + /* + * Record the beginning of optimizations; this turns on certain checks that + * guard against problematic late construction of things like class + * literals. + */ + jprogram.beginOptimizations(); + + boolean didChange; + do { + if (Thread.interrupted()) { + throw new InterruptedException(); + } + + // Recompute clinits each time, they can become empty. + jprogram.typeOracle.recomputeClinits(); + + didChange = false; + // Remove unreferenced types, fields, methods, [params, locals] + didChange = Pruner.exec(jprogram, true) || didChange; + // finalize locals, params, fields, methods, classes + didChange = Finalizer.exec(jprogram) || didChange; + // rewrite non-polymorphic calls as static calls; update all call sites + didChange = MakeCallsStatic.exec(jprogram) || didChange; + + // type flow tightening + // - fields, locals based on assignment + // - params based on assignment and call sites + // - method bodies based on return statements + // - polymorphic methods based on return types of all implementors + // - optimize casts and instance of + didChange = TypeTightener.exec(jprogram) || didChange; + + // tighten method call bindings + didChange = MethodCallTightener.exec(jprogram) || didChange; + + // dead code removal?? + didChange = DeadCodeElimination.exec(jprogram) || didChange; + + if (options.isAggressivelyOptimize()) { + // inlining + didChange = MethodInliner.exec(jprogram) || didChange; + } + // prove that any types that have been culled from the main tree are + // unreferenced due to type tightening? + } while (didChange); + } + + private static void checkForErrors(TreeLogger logger, + CompilationUnitDeclaration[] cuds, boolean itemizeErrors) + throws UnableToCompleteException { + boolean compilationFailed = false; + if (cuds.length == 0) { + compilationFailed = true; + } + Set<IProblem> problemSet = new HashSet<IProblem>(); + for (CompilationUnitDeclaration cud : cuds) { + CompilationResult result = cud.compilationResult(); + if (result.hasErrors()) { + compilationFailed = true; + // Early out if we don't need to itemize. + if (!itemizeErrors) { + break; + } + TreeLogger branch = logger.branch(TreeLogger.ERROR, "Errors in " + + String.valueOf(result.getFileName()), null); + IProblem[] errors = result.getErrors(); + for (IProblem problem : errors) { + if (problemSet.contains(problem)) { + continue; + } + + problemSet.add(problem); + + // Strip the initial code from each error. + // + String msg = problem.toString(); + msg = msg.substring(msg.indexOf(' ')); + + // Append 'file (line): msg' to the error message. + // + int line = problem.getSourceLineNumber(); + StringBuffer msgBuf = new StringBuffer(); + msgBuf.append("Line "); + msgBuf.append(line); + msgBuf.append(": "); + msgBuf.append(msg); + branch.log(TreeLogger.ERROR, msgBuf.toString(), null); + } + } + } + if (compilationFailed) { + logger.log(TreeLogger.ERROR, "Cannot proceed due to previous errors", + null); + throw new UnableToCompleteException(); + } + } + private static JMethodCall createReboundModuleLoad(TreeLogger logger, JProgram program, JReferenceType reboundEntryType, String originalMainClassName) throws UnableToCompleteException { @@ -127,9 +466,9 @@ + originalMainClassName + "' must not be abstract", null); throw new UnableToCompleteException(); } - SourceInfo sourceInfo = reboundEntryType.getSourceInfo().makeChild( JavaToJavaScriptCompiler.class, "Rebound entry point"); + JExpression qualifier = null; if (!entryMethod.isStatic()) { qualifier = JGwtCreate.createInstantiationExpression(program, sourceInfo, @@ -151,11 +490,10 @@ private static void findEntryPoints(TreeLogger logger, RebindPermutationOracle rpo, String[] mainClassNames, JProgram program) throws UnableToCompleteException { - JMethod bootStrapMethod = program.createMethod( - program.createSourceInfoSynthetic(JavaToJavaScriptCompiler.class, - "Bootstrap method"), "init".toCharArray(), - program.getIndexedType("EntryMethodHolder"), program.getTypeVoid(), - false, true, true, false, false); + SourceInfo sourceInfo = program.createSourceInfoSynthetic(JavaToJavaScriptCompiler.class, + "Bootstrap method"); + JMethod bootStrapMethod = program.createMethod(sourceInfo, "init".toCharArray(), + null, program.getTypeVoid(), false, true, true, false, false); bootStrapMethod.freezeParamTypes(); JMethodBody body = (JMethodBody) bootStrapMethod.getBody(); @@ -232,466 +570,15 @@ } private static long getUsedMemory() { + System.gc(); long used = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory(); assert (used > 0); return used; } - /** - * Create a variable assignment to invoke a call to the statistics collector. - * - * <pre> - * Stats.isStatsAvailable() && - * Stats.onModuleStart("mainClassName"); - * </pre> - */ - private static JStatement makeStatsCalls(JProgram program, - String mainClassName) { - SourceInfo sourceInfo = program.createSourceInfoSynthetic( - JavaToJavaScriptCompiler.class, "onModuleStart() stats call"); - JMethod isStatsAvailableMethod = program.getIndexedMethod("Stats.isStatsAvailable"); - JMethod onModuleStartMethod = program.getIndexedMethod("Stats.onModuleStart"); - - JMethodCall availableCall = new JMethodCall(program, sourceInfo, null, - isStatsAvailableMethod); - JMethodCall onModuleStartCall = new JMethodCall(program, sourceInfo, null, - onModuleStartMethod); - onModuleStartCall.getArgs().add( - program.getLiteralString(sourceInfo, mainClassName)); - - JBinaryOperation amp = new JBinaryOperation(program, sourceInfo, - program.getTypePrimitiveBoolean(), JBinaryOperator.AND, availableCall, - onModuleStartCall); - - return amp.makeStatement(); - } - - private final long astMemoryUsage; - private final String[] declEntryPoints; - private final Object myLockObject = new Object(); - private final JJSOptions options; - private final Set<IProblem> problemSet = new HashSet<IProblem>(); - private JProgram savedJProgram = null; - private JsProgram savedJsProgram = null; - private final byte[] serializedAst; - - public JavaToJavaScriptCompiler(TreeLogger logger, - WebModeCompilerFrontEnd compiler, String[] declEntryPts) - throws UnableToCompleteException { - this(logger, compiler, declEntryPts, new JJSOptions()); - } - - public JavaToJavaScriptCompiler(TreeLogger logger, - WebModeCompilerFrontEnd compiler, String[] declEntryPts, - JJSOptions compilerOptions) throws UnableToCompleteException { - - if (declEntryPts.length == 0) { - throw new IllegalArgumentException("entry point(s) required"); - } - - this.options = new JJSOptions(compilerOptions); - - // Remember these for subsequent compiles. - // - this.declEntryPoints = declEntryPts; - - RebindPermutationOracle rpo = compiler.getRebindPermutationOracle(); - - if (!options.isValidateOnly()) { - // Find all the possible rebound entry points. - Set<String> allEntryPoints = new TreeSet<String>(); - for (String element : declEntryPts) { - String[] all = rpo.getAllPossibleRebindAnswers(logger, element); - Util.addAll(allEntryPoints, all); - } - allEntryPoints.addAll(JProgram.CODEGEN_TYPES_SET); - allEntryPoints.addAll(JProgram.INDEX_TYPES_SET); - declEntryPts = allEntryPoints.toArray(new String[0]); - } - - // Compile the source and get the compiler so we can get the parse tree - // - CompilationUnitDeclaration[] goldenCuds = compiler.getCompilationUnitDeclarations( - logger, declEntryPts); - - // Check for compilation problems. We don't log here because any problems - // found here will have already been logged by AbstractCompiler. - // - checkForErrors(logger, goldenCuds, false); - - PerfLogger.start("Build AST"); - boolean enableDescendants = compilerOptions.getSoycOutputDir() != null; - JProgram jprogram = savedJProgram = new JProgram(); - jprogram.setEnableSourceInfoDescendants(enableDescendants); - JsProgram jsProgram = savedJsProgram = new JsProgram(); - jsProgram.setEnableSourceInfoDescendants(enableDescendants); - - long memoryDelta; - try { - System.gc(); - long usedMemoryBefore = getUsedMemory(); - /* - * (1) Build a flattened map of TypeDeclarations => JType. The resulting - * map contains entries for all reference types. BuildTypeMap also parses - * all JSNI. - */ - TypeMap typeMap = new TypeMap(jprogram); - TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap, - goldenCuds, jsProgram); - - // BuildTypeMap can uncover syntactic JSNI errors; report & abort - checkForErrors(logger, goldenCuds, true); - - // Compute all super type/sub type info - jprogram.typeOracle.computeBeforeAST(); - - // (2) Create our own Java AST from the JDT AST. - GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, jsProgram, - options.isEnableAssertions()); - - System.gc(); - long usedMemoryAfter = getUsedMemory(); - memoryDelta = usedMemoryAfter - usedMemoryBefore; - long localAstMemoryUsage = (long) (memoryDelta * 1.5); - - // GenerateJavaAST can uncover semantic JSNI errors; report & abort - checkForErrors(logger, goldenCuds, true); - - // Allow GC - goldenCuds = null; - typeMap = null; - allTypeDeclarations = null; - - // (3) Perform Java AST normalizations. - - FixAssignmentToUnbox.exec(jprogram); - - /* - * TODO: If we defer this until later, we could maybe use the results of - * the assertions to enable more optimizations. - */ - if (options.isEnableAssertions()) { - // Turn into assertion checking calls. - AssertionNormalizer.exec(jprogram); - } else { - // Remove all assert statements. - AssertionRemover.exec(jprogram); - } - - // Replace GWT.create calls with JGwtCreate nodes. - ReplaceRebinds.exec(logger, jprogram, rpo); - - // Resolve entry points, rebinding non-static entry points. - findEntryPoints(logger, rpo, declEntryPoints, jprogram); - - // Replace references to JSO subtypes with JSO itself. - JavaScriptObjectNormalizer.exec(jprogram); - - /* - * (4) Optimize the normalized Java AST for the common AST. By doing - * optimizations early in the multiple permutation scenario, we're saving - * work. However, we can't fully optimized because we don't yet know the - * deferred binding decisions. - * - * Don't bother optimizing early if there's only one permutation. - */ - if (rpo.getPermuationCount() > 1) { - optimize(jprogram); - - PerfLogger.start("serialize"); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream os = new ObjectOutputStream(baos); - os.writeObject(jprogram); - os.writeObject(jsProgram); - os.close(); - serializedAst = baos.toByteArray(); - PerfLogger.end(); - - // Very rough heuristic. - this.astMemoryUsage = Math.max(localAstMemoryUsage, - serializedAst.length * 4); - logger.log(TreeLogger.TRACE, "Estimated AST memory usage: " - + astMemoryUsage + " = Math.max(" + memoryDelta + " * 1.5, " - + serializedAst.length + " * 4)"); - } else { - this.astMemoryUsage = localAstMemoryUsage; - this.serializedAst = null; - } - } catch (IOException e) { - throw new RuntimeException( - "Should be impossible to get an IOException reading an in-memory stream", - e); - } catch (Throwable e) { - throw logAndTranslateException(logger, e); - } finally { - PerfLogger.end(); - synchronized (myLockObject) { - /* - * JLS 17.4.4: ensure all changes are visible to any other thread - * calling compile. - * - * TODO: is this necessary? - */ - } - } - } - - /** - * Creates finished JavaScript source code from the specified Java compilation - * units. - */ - public String compile(TreeLogger logger, RebindOracle rebindOracle) - throws UnableToCompleteException { - - JProgram jprogram = null; - JsProgram jsProgram = null; - - synchronized (myLockObject) { - if (savedJProgram != null && savedJsProgram != null) { - jprogram = savedJProgram; - jsProgram = savedJsProgram; - savedJProgram = null; - savedJsProgram = null; - } else { - if (serializedAst == null) { - throw new IllegalStateException("No serialized AST was cached."); - } - try { - /* - * Force all AST deserializations to occur in sequence; this reduces - * the chance of multiple threads going OOM at the same time. - */ - synchronized (myLockObject) { - PerfLogger.start("deserialize"); - ByteArrayInputStream bais = new ByteArrayInputStream(serializedAst); - ObjectInputStream is; - is = new ObjectInputStream(bais); - jprogram = (JProgram) is.readObject(); - jsProgram = (JsProgram) is.readObject(); - PerfLogger.end(); - } - } catch (IOException e) { - throw new RuntimeException( - "Should be impossible for memory based streams", e); - } catch (ClassNotFoundException e) { - throw new RuntimeException( - "Should be impossible when deserializing in process", e); - } - } - } - - try { - return doCompile(logger, jprogram, jsProgram, rebindOracle); - } catch (Throwable e) { - // Allow GC before logging exception in case it was an OOM. - jprogram = null; - jsProgram = null; - throw logAndTranslateException(logger, e); - } - } - - public long getAstMemoryUsage() { - return astMemoryUsage; - } - - protected String doCompile(TreeLogger logger, JProgram jprogram, - JsProgram jsProgram, RebindOracle rebindOracle) - throws InterruptedException { - if (JProgram.isTracingEnabled()) { - System.out.println("------------------------------------------------------------"); - System.out.println("| (new permuation) |"); - System.out.println("------------------------------------------------------------"); - } - - ResolveRebinds.exec(logger, jprogram, rebindOracle); - - // (4) Optimize the normalized Java AST for each permutation. - optimize(jprogram); - - // (5) "Normalize" the high-level Java tree into a lower-level tree more - // suited for JavaScript code generation. Don't go reordering these - // willy-nilly because there are some subtle interdependencies. - LongCastNormalizer.exec(jprogram); - JsoDevirtualizer.exec(jprogram); - CatchBlockNormalizer.exec(jprogram); - PostOptimizationCompoundAssignmentNormalizer.exec(jprogram); - LongEmulationNormalizer.exec(jprogram); - CastNormalizer.exec(jprogram); - ArrayNormalizer.exec(jprogram); - EqualityNormalizer.exec(jprogram); - - // (6) Perform further post-normalization optimizations - // Prune everything - Pruner.exec(jprogram, false); - - // (7) Generate a JavaScript code DOM from the Java type declarations - jprogram.typeOracle.recomputeClinits(); - GenerateJavaScriptAST.exec(jprogram, jsProgram, options.getOutput()); - - // Allow GC. - jprogram = null; - - // (8) Normalize the JS AST. - // Fix invalid constructs created during JS AST gen. - JsNormalizer.exec(jsProgram); - // Resolve all unresolved JsNameRefs. - JsSymbolResolver.exec(jsProgram); - - // (9) Optimize the JS AST. - if (options.isAggressivelyOptimize()) { - boolean didChange; - do { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - - didChange = false; - // Remove unused functions, possible - didChange = JsStaticEval.exec(jsProgram) || didChange; - // Inline JavaScript function invocations - didChange = JsInliner.exec(jsProgram) || didChange; - // Remove unused functions, possible - didChange = JsUnusedFunctionRemover.exec(jsProgram) || didChange; - } while (didChange); - } - - // (10) Obfuscate - switch (options.getOutput()) { - case OBFUSCATED: - JsStringInterner.exec(jsProgram); - JsObfuscateNamer.exec(jsProgram); - break; - case PRETTY: - // We don't intern strings in pretty mode to improve readability - JsPrettyNamer.exec(jsProgram); - break; - case DETAILED: - JsStringInterner.exec(jsProgram); - JsVerboseNamer.exec(jsProgram); - break; - default: - throw new InternalCompilerException("Unknown output mode"); - } - - // (11) Perform any post-obfuscation normalizations. - - // Work around an IE7 bug, - // http://code.google.com/p/google-web-toolkit/issues/detail?id=1440 - JsIEBlockSizeVisitor.exec(jsProgram); - - // Write the SOYC reports into the output - if (options.getSoycOutputDir() != null) { - SourceInfoHistogram.exec(jsProgram, options.getSoycOutputDir()); - } - - // (12) Generate the final output text. - DefaultTextOutput out = new DefaultTextOutput( - options.getOutput().shouldMinimize()); - JsSourceGenerationVisitor v = new JsSourceGenerationVisitor(out); - v.accept(jsProgram); - return out.toString(); - } - - protected void optimize(JProgram jprogram) throws InterruptedException { - /* - * Record the beginning of optimations; this turns on certain checks that - * guard against problematic late construction of things like class - * literals. - */ - jprogram.beginOptimizations(); - - boolean didChange; - do { - if (Thread.interrupted()) { - throw new InterruptedException(); - } - - // Recompute clinits each time, they can become empty. - jprogram.typeOracle.recomputeClinits(); - - didChange = false; - // Remove unreferenced types, fields, methods, [params, locals] - didChange = Pruner.exec(jprogram, true) || didChange; - // finalize locals, params, fields, methods, classes - didChange = Finalizer.exec(jprogram) || didChange; - // rewrite non-polymorphic calls as static calls; update all call sites - didChange = MakeCallsStatic.exec(jprogram) || didChange; - - // type flow tightening - // - fields, locals based on assignment - // - params based on assignment and call sites - // - method bodies based on return statements - // - polymorphic methods based on return types of all implementors - // - optimize casts and instance of - didChange = TypeTightener.exec(jprogram) || didChange; - - // tighten method call bindings - didChange = MethodCallTightener.exec(jprogram) || didChange; - - // dead code removal?? - didChange = DeadCodeElimination.exec(jprogram) || didChange; - - if (options.isAggressivelyOptimize()) { - // inlining - didChange = MethodInliner.exec(jprogram) || didChange; - } - // prove that any types that have been culled from the main tree are - // unreferenced due to type tightening? - } while (didChange); - } - - private void checkForErrors(TreeLogger logger, - CompilationUnitDeclaration[] cuds, boolean itemizeErrors) - throws UnableToCompleteException { - boolean compilationFailed = false; - if (cuds.length == 0) { - compilationFailed = true; - } - for (CompilationUnitDeclaration cud : cuds) { - CompilationResult result = cud.compilationResult(); - if (result.hasErrors()) { - compilationFailed = true; - // Early out if we don't need to itemize. - if (!itemizeErrors) { - break; - } - TreeLogger branch = logger.branch(TreeLogger.ERROR, "Errors in " - + String.valueOf(result.getFileName()), null); - IProblem[] errors = result.getErrors(); - for (IProblem problem : errors) { - if (problemSet.contains(problem)) { - continue; - } - - problemSet.add(problem); - - // Strip the initial code from each error. - // - String msg = problem.toString(); - msg = msg.substring(msg.indexOf(' ')); - - // Append 'file (line): msg' to the error message. - // - int line = problem.getSourceLineNumber(); - StringBuffer msgBuf = new StringBuffer(); - msgBuf.append("Line "); - msgBuf.append(line); - msgBuf.append(": "); - msgBuf.append(msg); - branch.log(TreeLogger.ERROR, msgBuf.toString(), null); - } - } - } - if (compilationFailed) { - logger.log(TreeLogger.ERROR, "Cannot proceed due to previous errors", - null); - throw new UnableToCompleteException(); - } - } - - private UnableToCompleteException logAndTranslateException(TreeLogger logger, - Throwable e) { + private static UnableToCompleteException logAndTranslateException( + TreeLogger logger, Throwable e) { if (e instanceof UnableToCompleteException) { // just rethrow return (UnableToCompleteException) e; @@ -732,4 +619,33 @@ return new UnableToCompleteException(); } } + + /** + * Create a variable assignment to invoke a call to the statistics collector. + * + * <pre> + * Stats.isStatsAvailable() && + * Stats.onModuleStart("mainClassName"); + * </pre> + */ + private static JStatement makeStatsCalls(JProgram program, + String mainClassName) { + SourceInfo sourceInfo = program.createSourceInfoSynthetic( + JavaToJavaScriptCompiler.class, "onModuleStart() stats call"); + JMethod isStatsAvailableMethod = program.getIndexedMethod("Stats.isStatsAvailable"); + JMethod onModuleStartMethod = program.getIndexedMethod("Stats.onModuleStart"); + + JMethodCall availableCall = new JMethodCall(program, sourceInfo, null, + isStatsAvailableMethod); + JMethodCall onModuleStartCall = new JMethodCall(program, sourceInfo, null, + onModuleStartMethod); + onModuleStartCall.getArgs().add(program.getLiteralString(sourceInfo, mainClassName)); + + JBinaryOperation amp = new JBinaryOperation(program, sourceInfo, + program.getTypePrimitiveBoolean(), JBinaryOperator.AND, availableCall, + onModuleStartCall); + + return amp.makeStatement(); + } + }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java b/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java new file mode 100644 index 0000000..a5e85b0 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
@@ -0,0 +1,208 @@ +/* + * 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 com.google.gwt.dev.jjs.ast.JProgram; +import com.google.gwt.dev.js.ast.JsProgram; +import com.google.gwt.dev.util.PerfLogger; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.Collections; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * Represents a unified, non-permutation specific AST. This AST is used to drive + * per-permutation compiles. + */ +public final class UnifiedAst implements Serializable { + + /** + * Encapsulates the combined programs. + */ + static final class AST { + private JProgram jProgram; + private JsProgram jsProgram; + + public AST(JProgram jProgram, JsProgram jsProgram) { + this.jProgram = jProgram; + this.jsProgram = jsProgram; + } + + JProgram getJProgram() { + return jProgram; + } + + JsProgram getJsProgram() { + return jsProgram; + } + } + + /** + * Estimated AST memory usage. + */ + private long astMemoryUsage; + + /** + * The original AST; nulled out once consumed (by the first call to + * {@link #getFreshAst()}. + */ + private transient AST initialAst; + + /** + * Used for internal synchronization. + */ + private transient Object myLockObject = new Object(); + + /** + * The compilation options. + */ + private final JJSOptions options; + + /** + * The set of all live rebind request types in the AST. + */ + private final SortedSet<String> rebindRequests; + + /** + * The serialized form of savedAst. + */ + private byte[] serializedAst; + + /** + * If <code>true</code>, only one permutation will be run, so we don't need + * to serialize our AST (unless this whole object is about to be serialized). + */ + private transient boolean singlePermutation; + + public UnifiedAst(JJSOptions options, AST initialAst, + boolean singlePermutation, long astMemoryUsage, Set<String> rebindRequests) { + this.options = new JJSOptionsImpl(options); + this.initialAst = initialAst; + this.singlePermutation = singlePermutation; + this.astMemoryUsage = astMemoryUsage; + this.rebindRequests = Collections.unmodifiableSortedSet(new TreeSet<String>( + rebindRequests)); + } + + /** + * Returns a rough estimate of how much memory an AST will take up. + */ + public long getAstMemoryUsage() { + return astMemoryUsage; + } + + /** + * Returns the active set of JJS options associated with this compile. + */ + public JJSOptions getOptions() { + return new JJSOptionsImpl(options); + } + + /** + * Returns the set of live rebind requests in the AST. + */ + public SortedSet<String> getRebindRequests() { + return rebindRequests; + } + + AST getFreshAst() { + synchronized (myLockObject) { + if (initialAst != null) { + if (!singlePermutation && serializedAst == null) { + // Must preserve a serialized copy for future calls. + serializeAst(); + } + AST result = initialAst; + initialAst = null; + return result; + } else { + if (serializedAst == null) { + throw new IllegalStateException("No serialized AST was cached."); + } + return deserializeAst(); + } + } + } + + private AST deserializeAst() { + try { + PerfLogger.start("deserialize"); + ByteArrayInputStream bais = new ByteArrayInputStream(serializedAst); + ObjectInputStream is; + is = new ObjectInputStream(bais); + JProgram jprogram = (JProgram) is.readObject(); + JsProgram jsProgram = (JsProgram) is.readObject(); + return new AST(jprogram, jsProgram); + } catch (IOException e) { + throw new RuntimeException( + "Should be impossible for memory based streams", e); + } catch (ClassNotFoundException e) { + throw new RuntimeException( + "Should be impossible when deserializing in process", e); + } finally { + PerfLogger.end(); + } + } + + /** + * Re-initialize lock object. + */ + private Object readResolve() { + myLockObject = new Object(); + return this; + } + + private void serializeAst() { + try { + assert (initialAst != null); + assert (serializedAst == null); + PerfLogger.start("serialize"); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream os = new ObjectOutputStream(baos); + os.writeObject(initialAst.getJProgram()); + os.writeObject(initialAst.getJsProgram()); + os.close(); + serializedAst = baos.toByteArray(); + + // Very rough heuristic. + astMemoryUsage = Math.max(astMemoryUsage, serializedAst.length * 4); + } catch (IOException e) { + throw new RuntimeException( + "Should be impossible for memory based streams", e); + } finally { + PerfLogger.end(); + } + } + + /** + * Force byte serialization of AST before writing. + */ + private Object writeReplace() { + synchronized (myLockObject) { + if (serializedAst == null) { + serializeAst(); + } + } + return this; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java index eaab6ad..3a86ae0 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MethodInliner.java
@@ -116,6 +116,9 @@ && !stmts.isEmpty()) { // clinit() calls cannot be inlined unless they are empty possibleToInline = false; + } else if (!body.locals.isEmpty()) { + // methods with local variables cannot be inlined + possibleToInline = false; } else { JMultiExpression multi = createMultiExpressionFromBody(body, ignoringReturnValueFor == x); @@ -474,6 +477,7 @@ this.method = method; } + @Override public void endVisit(JMethodCall x, Context ctx) { if (x.getTarget() == method) { isRecursive = true;
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java index f3bb111..ce55dad 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/Pruner.java
@@ -479,7 +479,7 @@ @Override public boolean visit(JBinaryOperation x, Context ctx) { // special string concat handling - if (x.getOp() == JBinaryOperator.ADD + if ((x.getOp() == JBinaryOperator.ADD || x.getOp() == JBinaryOperator.ASG_ADD) && x.getType() == program.getTypeJavaLangString()) { rescueByConcat(x.getLhs().getType()); rescueByConcat(x.getRhs().getType());
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/RecordRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/RecordRebinds.java new file mode 100644 index 0000000..489043b --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/RecordRebinds.java
@@ -0,0 +1,62 @@ +/* + * 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.impl; + +import com.google.gwt.dev.jjs.ast.Context; +import com.google.gwt.dev.jjs.ast.JGwtCreate; +import com.google.gwt.dev.jjs.ast.JProgram; +import com.google.gwt.dev.jjs.ast.JReboundEntryPoint; +import com.google.gwt.dev.jjs.ast.JVisitor; + +import java.util.Set; + +/** + * Records all live rebinds. + */ +public class RecordRebinds { + + private class RebindVisitor extends JVisitor { + @Override + public void endVisit(JGwtCreate x, Context ctx) { + String reqType = x.getSourceType().getName().replace('$', '.'); + liveRebindRequests.add(reqType); + } + + @Override + public void endVisit(JReboundEntryPoint x, Context ctx) { + String reqType = x.getSourceType().getName().replace('$', '.'); + liveRebindRequests.add(reqType); + } + } + + public static void exec(JProgram program, Set<String> liveRebindRequests) { + new RecordRebinds(program, liveRebindRequests).execImpl(); + } + + private final JProgram program; + private final Set<String> liveRebindRequests; + + private RecordRebinds(JProgram program, Set<String> liveRebindRequests) { + this.program = program; + this.liveRebindRequests = liveRebindRequests; + } + + private void execImpl() { + RebindVisitor rebinder = new RebindVisitor(); + rebinder.accept(program); + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java index d0cbdd7..841735a 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ResolveRebinds.java
@@ -15,9 +15,6 @@ */ package com.google.gwt.dev.jjs.impl; -import com.google.gwt.core.ext.TreeLogger; -import com.google.gwt.core.ext.UnableToCompleteException; -import com.google.gwt.dev.jdt.RebindOracle; import com.google.gwt.dev.jjs.InternalCompilerException; import com.google.gwt.dev.jjs.ast.Context; import com.google.gwt.dev.jjs.ast.JClassType; @@ -29,6 +26,7 @@ import com.google.gwt.dev.jjs.ast.JType; import java.util.List; +import java.util.Map; /** * Replaces any "GWT.create()" calls with a new expression for the actual result @@ -70,29 +68,23 @@ } } - public static boolean exec(TreeLogger logger, JProgram program, - RebindOracle rebindOracle) { - return new ResolveRebinds(logger, program, rebindOracle).execImpl(); + public static boolean exec(JProgram program, Map<String, String> rebindAnswers) { + return new ResolveRebinds(program, rebindAnswers).execImpl(); } - private final TreeLogger logger; private final JProgram program; - private final RebindOracle rebindOracle; + private final Map<String, String> rebindAnswers; - private ResolveRebinds(TreeLogger logger, JProgram program, - RebindOracle rebindOracle) { - this.logger = logger; + private ResolveRebinds(JProgram program, Map<String, String> rebindAnswers) { this.program = program; - this.rebindOracle = rebindOracle; + this.rebindAnswers = rebindAnswers; } public JClassType rebind(JType type) { // Rebinds are always on a source type name. String reqType = type.getName().replace('$', '.'); - String reboundClassName; - try { - reboundClassName = rebindOracle.rebind(logger, reqType); - } catch (UnableToCompleteException e) { + String reboundClassName = rebindAnswers.get(reqType); + if (reboundClassName == null) { // The fact that we already compute every rebind permutation before // compiling should prevent this case from ever happening in real life. //
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java index a8262fb..749bd5a 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java +++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -652,9 +652,13 @@ * Find a replacement method. If the original method is abstract, this will * return the leaf, final implementation of the method. If the method is * already concrete, but enclosed by an abstract type, the overriding method - * from the leaf concrete type will be returned. + * from the leaf concrete type will be returned. If the method is static, + * return <code>null</code> no matter what. */ private JMethod getSingleConcreteMethod(JMethod method) { + if (method.isStatic()) { + return null; + } if (getSingleConcreteType(method.getEnclosingType()) != null) { return getSingleConcrete(method, overriders); } else {
diff --git a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java index b36d31c..670af09 100644 --- a/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java +++ b/dev/core/src/com/google/gwt/dev/shell/GWTShellServlet.java
@@ -22,7 +22,7 @@ import com.google.gwt.dev.GWTShell; import com.google.gwt.dev.cfg.ModuleDef; import com.google.gwt.dev.cfg.ModuleDefLoader; -import com.google.gwt.dev.jjs.JJSOptions; +import com.google.gwt.dev.jjs.JJSOptionsImpl; import com.google.gwt.dev.resource.Resource; import com.google.gwt.dev.util.HttpHeaders; import com.google.gwt.dev.util.Util; @@ -547,7 +547,7 @@ "Generating a script selection script for module " + moduleName); StandardLinkerContext context = new StandardLinkerContext(logger, - getModuleDef(logger, moduleName), null, null, new JJSOptions()); + getModuleDef(logger, moduleName), new JJSOptionsImpl()); HostedModeLinker linker = new HostedModeLinker(); return linker.generateSelectionScript(logger, context, context.getArtifacts());
diff --git a/dev/core/src/com/google/gwt/dev/util/Util.java b/dev/core/src/com/google/gwt/dev/util/Util.java index 6f3d637..aabd864 100644 --- a/dev/core/src/com/google/gwt/dev/util/Util.java +++ b/dev/core/src/com/google/gwt/dev/util/Util.java
@@ -40,11 +40,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.io.Reader; +import java.io.Serializable; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; @@ -140,6 +143,19 @@ return toHexString(md5.digest()); } + public static void copy(InputStream is, OutputStream os) throws IOException { + try { + byte[] buf = new byte[8 * 1024]; + int i; + while ((i = is.read(buf)) != -1) { + os.write(buf, 0, i); + } + } finally { + Utility.close(is); + Utility.close(os); + } + } + public static boolean copy(TreeLogger logger, File in, File out) throws UnableToCompleteException { try { @@ -548,6 +564,43 @@ return null; } + public static <T extends Serializable> T readFileAsObject(File file, + Class<T> type) throws ClassNotFoundException { + FileInputStream fileInputStream = null; + ObjectInputStream objectInputStream = null; + try { + fileInputStream = new FileInputStream(file); + objectInputStream = new ObjectInputStream(fileInputStream); + return type.cast(objectInputStream.readObject()); + } catch (IOException e) { + return null; + } finally { + Utility.close(objectInputStream); + Utility.close(fileInputStream); + } + } + + public static Serializable[] readFileAsObjects(File file, + Class<? extends Serializable>... types) throws ClassNotFoundException { + FileInputStream fileInputStream = null; + ObjectInputStream objectInputStream = null; + try { + fileInputStream = new FileInputStream(file); + objectInputStream = new ObjectInputStream(fileInputStream); + Serializable[] results = new Serializable[types.length]; + for (int i = 0; i < results.length; ++i) { + Object object = objectInputStream.readObject(); + results[i] = types[i].cast(object); + } + return results; + } catch (IOException e) { + return null; + } finally { + Utility.close(objectInputStream); + Utility.close(fileInputStream); + } + } + public static String readFileAsString(File file) { byte[] bytes = readFileAsBytes(file); if (bytes != null) { @@ -974,24 +1027,26 @@ } } - public static void writeStringAsFile(TreeLogger logger, File file, - String string) throws UnableToCompleteException { + /** + * Serializes an object and writes it to a file. + */ + public static void writeObjectAsFile(TreeLogger logger, File file, + Serializable... objects) throws UnableToCompleteException { FileOutputStream stream = null; - OutputStreamWriter writer = null; - BufferedWriter buffered = null; + ObjectOutputStream objectStream = null; try { - stream = new FileOutputStream(file); - writer = new OutputStreamWriter(stream, DEFAULT_ENCODING); - buffered = new BufferedWriter(writer); file.getParentFile().mkdirs(); - buffered.write(string); + stream = new FileOutputStream(file); + objectStream = new ObjectOutputStream(stream); + for (Serializable object : objects) { + objectStream.writeObject(object); + } } catch (IOException e) { logger.log(TreeLogger.ERROR, "Unable to write file: " + file.getAbsolutePath(), e); throw new UnableToCompleteException(); } finally { - Utility.close(buffered); - Utility.close(writer); + Utility.close(objectStream); Utility.close(stream); } } @@ -1016,16 +1071,25 @@ return true; } - private static void copy(InputStream is, OutputStream os) throws IOException { + public static void writeStringAsFile(TreeLogger logger, File file, + String string) throws UnableToCompleteException { + FileOutputStream stream = null; + OutputStreamWriter writer = null; + BufferedWriter buffered = null; try { - byte[] buf = new byte[8 * 1024]; - int i; - while ((i = is.read(buf)) != -1) { - os.write(buf, 0, i); - } + stream = new FileOutputStream(file); + writer = new OutputStreamWriter(stream, DEFAULT_ENCODING); + buffered = new BufferedWriter(writer); + file.getParentFile().mkdirs(); + buffered.write(string); + } catch (IOException e) { + logger.log(TreeLogger.ERROR, "Unable to write file: " + + file.getAbsolutePath(), e); + throw new UnableToCompleteException(); } finally { - Utility.close(is); - Utility.close(os); + Utility.close(buffered); + Utility.close(writer); + Utility.close(stream); } }
diff --git a/dev/core/src/com/google/gwt/util/tools/ArgHandlerDisableAggressiveOptimization.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDisableAggressiveOptimization.java similarity index 69% rename from dev/core/src/com/google/gwt/util/tools/ArgHandlerDisableAggressiveOptimization.java rename to dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDisableAggressiveOptimization.java index f464ea4..b3b4579 100644 --- a/dev/core/src/com/google/gwt/util/tools/ArgHandlerDisableAggressiveOptimization.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDisableAggressiveOptimization.java
@@ -13,13 +13,22 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.util.tools; +package com.google.gwt.dev.util.arg; + +import com.google.gwt.util.tools.ArgHandlerFlag; /** * Handles the -XdisableAggressiveOptimization command line flag. */ -public abstract class ArgHandlerDisableAggressiveOptimization extends +public final class ArgHandlerDisableAggressiveOptimization extends ArgHandlerFlag { + private final OptionAggressivelyOptimize option; + + public ArgHandlerDisableAggressiveOptimization( + OptionAggressivelyOptimize option) { + this.option = option; + } + @Override public String getPurpose() { return "Troubleshooting: Prevent the web mode compiler from performing " @@ -35,4 +44,10 @@ public boolean isUndocumented() { return true; } + + @Override + public boolean setFlag() { + option.setAggressivelyOptimize(false); + return true; + } }
diff --git a/dev/core/src/com/google/gwt/util/tools/ArgHandlerEnableAssertions.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerEnableAssertions.java similarity index 72% rename from dev/core/src/com/google/gwt/util/tools/ArgHandlerEnableAssertions.java rename to dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerEnableAssertions.java index f37fa97..9697b1c 100644 --- a/dev/core/src/com/google/gwt/util/tools/ArgHandlerEnableAssertions.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerEnableAssertions.java
@@ -13,19 +13,19 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.google.gwt.util.tools; +package com.google.gwt.dev.util.arg; -import com.google.gwt.dev.jjs.JJSOptions; +import com.google.gwt.util.tools.ArgHandlerFlag; /** * Handles the -ea command line flag. */ -public class ArgHandlerEnableAssertions extends ArgHandlerFlag { +public final class ArgHandlerEnableAssertions extends ArgHandlerFlag { - private final JJSOptions optionsToModify; + private final OptionEnableAssertions option; - public ArgHandlerEnableAssertions(JJSOptions optionsToModify) { - this.optionsToModify = optionsToModify; + public ArgHandlerEnableAssertions(OptionEnableAssertions option) { + this.option = option; } @Override @@ -40,7 +40,7 @@ @Override public boolean setFlag() { - optionsToModify.setEnableAssertions(true); + option.setEnableAssertions(true); return true; } }
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerGenDir.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerGenDir.java index 07ed09c..e9bc414 100644 --- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerGenDir.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerGenDir.java
@@ -17,10 +17,18 @@ import com.google.gwt.util.tools.ArgHandlerDir; +import java.io.File; + /** - * Argument handler for processing the code generation directory flag. + * Argument handler for processing the code generation directory flag. */ -public abstract class ArgHandlerGenDir extends ArgHandlerDir { +public final class ArgHandlerGenDir extends ArgHandlerDir { + + private final OptionGenDir option; + + public ArgHandlerGenDir(OptionGenDir option) { + this.option = option; + } public String getPurpose() { return "The directory into which generated files will be written for review"; @@ -29,4 +37,9 @@ public String getTag() { return "-gen"; } + + @Override + public void setDir(File dir) { + option.setGenDir(dir); + } } \ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerLogLevel.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerLogLevel.java index 0008eb9..f2bf710 100644 --- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerLogLevel.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerLogLevel.java
@@ -21,7 +21,7 @@ /** * Argument handler for processing the log level flag. */ -public abstract class ArgHandlerLogLevel extends ArgHandler { +public final class ArgHandlerLogLevel extends ArgHandler { private static final String OPTIONS_STRING = computeOptionsString(); @@ -40,7 +40,13 @@ return sb.toString(); } - public final String[] getDefaultArgs() { + private final OptionLogLevel options; + + public ArgHandlerLogLevel(OptionLogLevel options) { + this.options = options; + } + + public String[] getDefaultArgs() { return new String[] {getTag(), getDefaultLogLevel().name()}; } @@ -60,7 +66,7 @@ if (startIndex + 1 < args.length) { try { Type level = Type.valueOf(args[startIndex + 1]); - setLogLevel(level); + options.setLogLevel(level); return 1; } catch (IllegalArgumentException e) { // Argument did not match any enum value; fall through to error case. @@ -72,8 +78,6 @@ return -1; } - public abstract void setLogLevel(Type level); - protected Type getDefaultLogLevel() { return Type.INFO; }
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerModuleName.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerModuleName.java new file mode 100644 index 0000000..3dab092 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerModuleName.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.dev.util.arg; + +import com.google.gwt.util.tools.ArgHandlerExtra; + +/** + * Argument handler for module name, which has no option profix. + */ +public final class ArgHandlerModuleName extends ArgHandlerExtra { + + private final OptionModuleName option; + + public ArgHandlerModuleName(OptionModuleName option) { + this.option = option; + } + + @Override + public boolean addExtraArg(String arg) { + option.setModuleName(arg); + return true; + } + + @Override + public String getPurpose() { + return "Specifies the name of the module to compile"; + } + + @Override + public String[] getTagArgs() { + return new String[] {"module"}; + } + + @Override + public boolean isRequired() { + return true; + } +} \ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerOutDir.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerOutDir.java new file mode 100644 index 0000000..bf5a5b1 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerOutDir.java
@@ -0,0 +1,50 @@ +/* + * Copyright 2006 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.ArgHandlerDir; + +import java.io.File; + +/** + * Argument handler for processing the output directory flag. + */ +public final class ArgHandlerOutDir extends ArgHandlerDir { + + private final OptionOutDir option; + + public ArgHandlerOutDir(OptionOutDir option) { + this.option = option; + } + + public String[] getDefaultArgs() { + return new String[] {"-out", System.getProperty("user.dir")}; + } + + public String getPurpose() { + return "The directory to write output files into (defaults to current)"; + } + + public String getTag() { + return "-out"; + } + + @Override + public void setDir(File dir) { + option.setOutDir(dir); + } + +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerScriptStyle.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerScriptStyle.java index fff73c9..3c9b5c0 100644 --- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerScriptStyle.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerScriptStyle.java
@@ -15,7 +15,6 @@ */ package com.google.gwt.dev.util.arg; -import com.google.gwt.dev.jjs.JJSOptions; import com.google.gwt.dev.jjs.JsOutputOption; import com.google.gwt.util.tools.ArgHandler; @@ -24,10 +23,10 @@ */ public final class ArgHandlerScriptStyle extends ArgHandler { - private final JJSOptions optionsToModify; + private final OptionScriptStyle option; - public ArgHandlerScriptStyle(JJSOptions optionsToModify) { - this.optionsToModify = optionsToModify; + public ArgHandlerScriptStyle(OptionScriptStyle option) { + this.option = option; } public String[] getDefaultArgs() { @@ -50,13 +49,13 @@ if (startIndex + 1 < args.length) { String style = args[startIndex + 1].toLowerCase(); if (style.startsWith("obf")) { - optionsToModify.setOutput(JsOutputOption.OBFUSCATED); + option.setOutput(JsOutputOption.OBFUSCATED); return 1; } else if ("pretty".equals(style)) { - optionsToModify.setOutput(JsOutputOption.PRETTY); + option.setOutput(JsOutputOption.PRETTY); return 1; } else if ("detailed".equals(style)) { - optionsToModify.setOutput(JsOutputOption.DETAILED); + option.setOutput(JsOutputOption.DETAILED); return 1; } }
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerTreeLoggerFlag.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerTreeLoggerFlag.java index a88facb..fe8331d 100644 --- a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerTreeLoggerFlag.java +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerTreeLoggerFlag.java
@@ -18,9 +18,15 @@ import com.google.gwt.util.tools.ArgHandlerFlag; /** - * Argument handler for processing the tree logger boolean flag. + * Argument handler for processing the GUI tree logger boolean flag. */ -public abstract class ArgHandlerTreeLoggerFlag extends ArgHandlerFlag { +public final class ArgHandlerTreeLoggerFlag extends ArgHandlerFlag { + + private final OptionGuiLogger option; + + public ArgHandlerTreeLoggerFlag(OptionGuiLogger option) { + this.option = option; + } public String getPurpose() { return "Logs output in a graphical tree view"; @@ -30,5 +36,8 @@ return "-treeLogger"; } - public abstract boolean setFlag(); + public boolean setFlag() { + option.setUseGuiLogger(true); + return true; + } } \ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerValidateOnlyFlag.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerValidateOnlyFlag.java new file mode 100644 index 0000000..7740786 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerValidateOnlyFlag.java
@@ -0,0 +1,43 @@ +/* + * 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; + +/** + * Handles the -validateOnly command line flag. + */ +public final class ArgHandlerValidateOnlyFlag extends ArgHandlerFlag { + + private final OptionValidateOnly option; + + public ArgHandlerValidateOnlyFlag(OptionValidateOnly option) { + this.option = option; + } + + public String getPurpose() { + return "Validate all source code, but do not compile"; + } + + public String getTag() { + return "-validateOnly"; + } + + public boolean setFlag() { + option.setValidateOnly(true); + return true; + } +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionAggressivelyOptimize.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionAggressivelyOptimize.java new file mode 100644 index 0000000..889a25f --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionAggressivelyOptimize.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.dev.util.arg; + +/** + * Option to determine whether the compiler should aggressively optimize. + */ +public interface OptionAggressivelyOptimize { + + /** + * Returns true if the compiler should aggressively optimize. + */ + boolean isAggressivelyOptimize(); + + /** + * Sets whether or not the compiler should aggressively optimize. + */ + void setAggressivelyOptimize(boolean aggressivelyOptimize); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionEnableAssertions.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionEnableAssertions.java new file mode 100644 index 0000000..609d95b --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionEnableAssertions.java
@@ -0,0 +1,33 @@ +/* + * 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 to determine whether the compiler should generate code to check + * assertions. + */ +public interface OptionEnableAssertions { + + /** + * Returns true if the compiler should generate code to check assertions. + */ + boolean isEnableAssertions(); + + /** + * Sets whether or not the compiler should generate code to check assertions. + */ + void setEnableAssertions(boolean enableAssertions); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionGenDir.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionGenDir.java new file mode 100644 index 0000000..ff2e253 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionGenDir.java
@@ -0,0 +1,34 @@ +/* + * 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 java.io.File; + +/** + * Option to set the generated resource directory. + */ +public interface OptionGenDir { + + /** + * Returns the generated resource directory. + */ + File getGenDir(); + + /** + * Sets the generated resource directory. + */ + void setGenDir(File dir); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionGuiLogger.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionGuiLogger.java new file mode 100644 index 0000000..56205ba --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionGuiLogger.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.dev.util.arg; + +/** + * Option to set whether to use a GUI logger instead of stdout. + */ +public interface OptionGuiLogger { + + /** + * Returns true if a GUI logger should be used. + */ + boolean isUseGuiLogger(); + + /** + * Sets whether or not to use a GUI logger. + */ + void setUseGuiLogger(boolean useGuiLogger); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionLogLevel.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionLogLevel.java new file mode 100644 index 0000000..ebef78d --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionLogLevel.java
@@ -0,0 +1,34 @@ +/* + * 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.core.ext.TreeLogger; + +/** + * Option to set the tree logger log level. + */ +public interface OptionLogLevel { + + /** + * Returns the tree logger level. + */ + TreeLogger.Type getLogLevel(); + + /** + * Sets the tree logger level. + */ + void setLogLevel(TreeLogger.Type logLevel); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionModuleName.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionModuleName.java new file mode 100644 index 0000000..688406e --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionModuleName.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.dev.util.arg; + +/** + * Option to set the module name. + */ +public interface OptionModuleName { + + /** + * Returns the name of the module. + */ + String getModuleName(); + + /** + * Sets the name of the module. + */ + void setModuleName(String moduleName); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionOutDir.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionOutDir.java new file mode 100644 index 0000000..25b2b12 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionOutDir.java
@@ -0,0 +1,34 @@ +/* + * 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 java.io.File; + +/** + * Option to set the output directory. + */ +public interface OptionOutDir { + + /** + * Returns the output directory. + */ + File getOutDir(); + + /** + * Sets the output directory. + */ + void setOutDir(File dir); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionScriptStyle.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionScriptStyle.java new file mode 100644 index 0000000..736f7e3 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionScriptStyle.java
@@ -0,0 +1,34 @@ +/* + * 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.dev.jjs.JsOutputOption; + +/** + * Option for setting the compiler output style. + */ +public interface OptionScriptStyle { + + /** + * Returns the compiler output style. + */ + JsOutputOption getOutput(); + + /** + * Sets the compiler output style. + */ + void setOutput(JsOutputOption obfuscated); +}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionValidateOnly.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionValidateOnly.java new file mode 100644 index 0000000..e402255 --- /dev/null +++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionValidateOnly.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.dev.util.arg; + +/** + * Option to set whether the compiler should validate and then quit. + */ +public interface OptionValidateOnly { + + /** + * Returns true the compiler should only validate. + */ + boolean isValidateOnly(); + + /** + * Sets whether or not the compiler should only validate. + */ + void setValidateOnly(boolean validateOnly); +}
diff --git a/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java b/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java index 09c8f21..ea2625c 100644 --- a/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java +++ b/dev/core/test/com/google/gwt/dev/shell/StandardGeneratorContextTest.java
@@ -155,6 +155,7 @@ tempOutDir = createTempDir("gwt-out-"); genCtx = new StandardGeneratorContext(mockCompilationState, mockPropOracle, mockPublicOracle, tempGenDir, tempOutDir, artifactSet); + genCtx.setCurrentGenerator(Generator.class); } public void testTryCreateResource_badFileName() { @@ -355,7 +356,8 @@ protected void tearDown() throws Exception { for (int i = toDelete.size() - 1; i >= 0; --i) { File f = toDelete.get(i); - assertTrue(f.delete()); + Util.recursiveDelete(f, false); + assertFalse("Unable to delete " + f.getAbsolutePath(), f.exists()); } }
diff --git a/distro-source/core/src/release_notes.html b/distro-source/core/src/release_notes.html index 37721ff..37c804b 100644 --- a/distro-source/core/src/release_notes.html +++ b/distro-source/core/src/release_notes.html
@@ -29,6 +29,7 @@ <h1>Google Web Toolkit Release Notes</h1> <ul> <li><a href="#Release_Notes_Current">@GWT_VERSION@</a></li> + <li><a href="#Release_Notes_1_5_2">1.5.2</a></li> <li><a href="#Release_Notes_1_5_1">1.5.1 (RC2)</a></li> <li><a href="#Release_Notes_1_5_0">1.5.0 (RC)</a></li> <li><a href="#Release_Notes_1_4_60">1.4.60</a></li> @@ -46,6 +47,25 @@ <hr/> <a name="Release_Notes_Current"></a> <h2>Release Notes for @GWT_VERSION@</h2> + <h3>Fixed Issues</h3> + <ul> + <li>RPC requests no longer fail on the embedded Android web browser</li> + <li>Leaf <code>TreeItems</code> now line up with their non-leaf siblings</li> + <li>Removing the last child node from a <code>TreeItem</code> no longer creates extra margins on the left</li> + <li><code>HTTPRequest</code> no longer uses POST instead of GET on some IE installs because of incorrect XHR selection</li> + <li>Compiler now uses a more reliable check to prevent methods with local variables from being inlined</li> + <li><code>getAbsoluteTop()/Left()</code> can no longer return non-integral values</li> + <li><code>Time.valueOf()</code> no longer fails to parse <code>"08:00:00"</code> or incorrectly accepts <code>"0xC:0xB:0xA"</code>.</li> + </ul> + <p> + See the GWT issue tracker for + <a href="http://code.google.com/p/google-web-toolkit/issues/list?can=1&q=status%3AFixed%2CFixedNotReleased%20milestone%3A1_5_3&num=1000"> + the complete list of bug fixes and enhancements</a> in this release. + </p> + + <hr/> + <a name="Release_Notes_1_5_2"></a> + <h2>Release Notes for 1.5.2</h2> <h3>Potentially breaking changes and fixes</h3> <ul> <li><code>History.onHistoryChanged()</code> has been added back (it was missing from 1.5 RC2) but is now deprecated. Application startup should be handled by calling the new <code>History.fireCurrentHistoryState()</code>.</li>
diff --git a/eclipse/samples/DynaTable2/.checkstyle b/eclipse/samples/DynaTable2/.checkstyle new file mode 100644 index 0000000..0f1cbd9 --- /dev/null +++ b/eclipse/samples/DynaTable2/.checkstyle
@@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<fileset-config file-format-version="1.2.0" simple-config="true"> + <fileset name="all" enabled="true" check-config-name="GWT Checks" local="false"> + <file-match-pattern match-pattern="." include-pattern="true"/> + </fileset> + <filter name="NonSrcDirs" enabled="true"/> +</fileset-config>
diff --git a/eclipse/samples/DynaTable2/.classpath b/eclipse/samples/DynaTable2/.classpath new file mode 100644 index 0000000..447a770 --- /dev/null +++ b/eclipse/samples/DynaTable2/.classpath
@@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="core/src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry combineaccessrules="false" kind="src" path="/gwt-user"/> + <classpathentry kind="output" path="war/WEB-INF/classes"/> +</classpath>
diff --git a/eclipse/samples/DynaTable2/.project b/eclipse/samples/DynaTable2/.project new file mode 100644 index 0000000..5ece582 --- /dev/null +++ b/eclipse/samples/DynaTable2/.project
@@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>DynaTable2</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature> + </natures> + <linkedResources> + <link> + <name>core</name> + <type>2</type> + <locationURI>GWT_ROOT/samples/dynatable</locationURI> + </link> + </linkedResources> +</projectDescription>
diff --git a/eclipse/samples/DynaTable2/DynaTable2 compile.launch b/eclipse/samples/DynaTable2/DynaTable2 compile.launch new file mode 100644 index 0000000..9d1d153 --- /dev/null +++ b/eclipse/samples/DynaTable2/DynaTable2 compile.launch
@@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/DynaTable2"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="4"/> +</listAttribute> +<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> +<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER" javaProject="DynaTable2" path="1" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/DynaTable2/core/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/super" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-dev-windows/core/super" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="DynaTable2"/> </runtimeClasspathEntry> "/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.GWTCompiler"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-wardir war -style PRETTY -logLevel INFO com.google.gwt.sample.dynatable.DynaTable"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="DynaTable2"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dgwt.devjar=C:\gwt\releases\1.6\build\staging\gwt-windows-0.0.0\gwt-dev-windows.jar"/> +</launchConfiguration>
diff --git a/eclipse/samples/DynaTable2/DynaTable2 server.launch b/eclipse/samples/DynaTable2/DynaTable2 server.launch new file mode 100644 index 0000000..4029b62 --- /dev/null +++ b/eclipse/samples/DynaTable2/DynaTable2 server.launch
@@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/DynaTable2"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="4"/> +</listAttribute> +<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> +<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER" javaProject="DynaTable2" path="1" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/DynaTable2/jetty-6.1.11.jar" path="3" type="2"/> "/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.mortbay.jetty.Main"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="8888 -webapp war"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="DynaTable2"/> +</launchConfiguration>
diff --git a/eclipse/samples/DynaTable2/DynaTable2 shell.launch b/eclipse/samples/DynaTable2/DynaTable2 shell.launch new file mode 100644 index 0000000..cca8023 --- /dev/null +++ b/eclipse/samples/DynaTable2/DynaTable2 shell.launch
@@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/DynaTable2"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="4"/> +</listAttribute> +<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/> +<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER" javaProject="DynaTable2" path="1" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/DynaTable2/core/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/super" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry internalArchive="/gwt-dev-windows/core/super" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="DynaTable2"/> </runtimeClasspathEntry> "/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.GWTShell"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-wardir war -style PRETTY -logLevel INFO DynaTable.html"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="DynaTable2"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Dgwt.devjar=C:\gwt\releases\1.6\build\staging\gwt-windows-0.0.0\gwt-dev-windows.jar"/> +</launchConfiguration>
diff --git a/eclipse/samples/DynaTable2/build.xml b/eclipse/samples/DynaTable2/build.xml new file mode 100644 index 0000000..d810bd9 --- /dev/null +++ b/eclipse/samples/DynaTable2/build.xml
@@ -0,0 +1,82 @@ +<project name="dynatable2" default="build" basedir="."> + <property name="gwt.install" location="../../../build/lib" /> + <property name="wardir" location="war" /> + + <target name="javac" description="Compile project to WEB-INF/classes"> + <mkdir dir="${wardir}/WEB-INF/classes" /> + <javac srcdir="../../../samples/dynatable/src" + destdir="${wardir}/WEB-INF/classes" + debug="true" + debuglevel="lines,vars,source" + source="1.5" + target="1.5" + nowarn="true" + encoding="utf-8"> + <classpath> + <pathelement location="${gwt.install}/gwt-user.jar" /> + <pathelement location="${gwt.install}/gwt-dev-windows.jar" /> + </classpath> + </javac> + </target> + + <target name="deploy" description="Copy output to the war folder"> + <mkdir dir="${wardir}/WEB-INF/lib" /> + <copy todir="${wardir}/WEB-INF/lib" file="${gwt.install}/gwt-servlet.jar" /> + </target> + + <target name="gwtc" depends="javac" description="Compile to JavaScript"> + <java classname="com.google.gwt.dev.GWTCompiler" fork="yes" failonerror="true"> + <jvmarg value="-Xmx256M"/> + <arg value="-wardir" /> + <arg file="war" /> + <arg value="com.google.gwt.sample.dynatable.DynaTable" /> + <classpath> + <pathelement location="../../../samples/dynatable/src" /> + <pathelement location="${wardir}/WEB-INF/classes" /> + <pathelement location="${gwt.install}/gwt-user.jar" /> + <pathelement location="${gwt.install}/gwt-dev-windows.jar" /> + </classpath> + </java> + </target> + + <target name="server" depends="deploy" description="Run the deployed app in a Jetty server"> + <echo message="PLEASE BROWSE TO: http://localhost:8888/DynaTable.html"/> + <java classname="org.mortbay.jetty.Main" fork="yes"> + <arg value="8888" /> + <arg value="-webapp" /> + <arg file="war" /> + <classpath> + <pathelement location="${gwt.install}/gwt-dev-windows.jar" /> + </classpath> + </java> + </target> + + <target name="shell" depends="javac" description="Run the deployed app in GWT hosted mode"> + <java classname="com.google.gwt.dev.GWTShell" fork="yes" failonerror="true"> + <jvmarg value="-Xmx256M"/> + <jvmarg value="-Dgwt.devjar=C:\gwt\releases\1.6\build\staging\gwt-windows-0.0.0\gwt-dev-windows.jar"/> + <arg value="-wardir" /> + <arg file="war" /> + <arg value="http://localhost:8888/DynaTable.html" /> + <classpath> + <pathelement location="../../../samples/dynatable/src" /> + <pathelement location="${wardir}/WEB-INF/classes" /> + <pathelement location="${gwt.install}/gwt-user.jar" /> + <pathelement location="${gwt.install}/gwt-dev-windows.jar" /> + </classpath> + </java> + </target> + + <target name="build" depends="javac, gwtc, deploy" description="Build this project" /> + + <target name="clean" description="Cleans this project's intermediate and output files"> + <delete includeemptydirs="true" failonerror="false"> + <fileset dir="${wardir}" includes="*" excludes="DynaTable.html"/> + </delete> + <delete dir="${wardir}/WEB-INF/classes" failonerror="false" /> + <delete dir="${wardir}/WEB-INF/lib" failonerror="false" /> + <delete dir="${wardir}/WEB-INF/gwt-aux" failonerror="false" /> + <delete dir="${wardir}/WEB-INF/.gwt-tmp" failonerror="false" /> + <delete dir="www" failonerror="false" /> + </target> +</project>
diff --git a/eclipse/samples/DynaTable2/war/WEB-INF/web.xml b/eclipse/samples/DynaTable2/war/WEB-INF/web.xml new file mode 100644 index 0000000..983aac0 --- /dev/null +++ b/eclipse/samples/DynaTable2/war/WEB-INF/web.xml
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<web-app> + + <!-- GWT REGENERATED BEGIN --> + <servlet> + <servlet-name>calendar</servlet-name> + <servlet-class>com.google.gwt.sample.dynatable.server.SchoolCalendarServiceImpl</servlet-class> + </servlet> + + <servlet-mapping> + <servlet-name>calendar</servlet-name> + <url-pattern>/calendar</url-pattern> + </servlet-mapping> + <!-- GWT REGENERATED END --> + +</web-app>
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java index 1fa5461..0d1d36b 100644 --- a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java +++ b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
@@ -26,9 +26,10 @@ // so we use getBoundingClientRect() whenever possible (but it's not // supported on older versions). If changing this code, make sure to check // the museum entry for issue 1932. + // (x) | 0 is used to coerce the value to an integer if (Element.prototype.getBoundingClientRect) { - return elem.getBoundingClientRect().left + - @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft; + return (elem.getBoundingClientRect().left + + @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollLeft) | 0; } else { // We cannot use DOMImpl here because offsetLeft/Top return erroneous // values when overflow is not visible. We have to difference screenX @@ -46,9 +47,10 @@ // so we use getBoundingClientRect() whenever possible (but it's not // supported on older versions). If changing this code, make sure to check // the museum entry for issue 1932. + // (x) | 0 is used to coerce the value to an integer if (Element.prototype.getBoundingClientRect) { - return elem.getBoundingClientRect().top + - @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop; + return (elem.getBoundingClientRect().top + + @com.google.gwt.user.client.impl.DocumentRootImpl::documentRoot.scrollTop) | 0; } else { // We cannot use DOMImpl here because offsetLeft/Top return erroneous // values when overflow is not visible. We have to difference screenX
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java index 71b44e2..8852eb9 100644 --- a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableImplCreator.java
@@ -15,6 +15,8 @@ */ package com.google.gwt.i18n.rebind; +import static com.google.gwt.i18n.rebind.AnnotationUtil.getClassAnnotation; + import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; @@ -139,7 +141,7 @@ context.commit(logger, pw); } // Generate a translatable output file if requested. - Generate generate = targetClass.getAnnotation(Generate.class); + Generate generate = getClassAnnotation(targetClass, Generate.class); if (generate != null) { String path = generate.fileName(); if (Generate.DEFAULT.equals(path)) {
diff --git a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java index 7a98120..8d1d141 100644 --- a/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/AbstractLocalizableInterfaceCreator.java
@@ -33,12 +33,10 @@ import java.io.PrintWriter; import java.io.Writer; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.Map.Entry; -import java.util.regex.Pattern; /** * Abstract base functionality for <code>MessagesInterfaceCreator</code> and @@ -50,20 +48,32 @@ @Override public String format(String key) { - if (methodNames.contains(key)) { + while (methodNames.contains(key)) { key += "_dup"; - return format(key); - } else { - methodNames.add(key); - return key; } + methodNames.add(key); + return key; } } private static class ReplaceBadChars extends ResourceKeyFormatter { @Override public String format(String key) { - return DEFAULT_CHARS.matcher(key).replaceAll("_"); + StringBuilder buf = new StringBuilder(); + int keyLen = key == null ? 0 : key.length(); + for (int i = 0; i < keyLen; i = key.offsetByCodePoints(i, 1)) { + int codePoint = key.codePointAt(i); + if (i == 0 ? Character.isJavaIdentifierStart(codePoint) + : Character.isJavaIdentifierPart(codePoint)) { + buf.appendCodePoint(codePoint); + } else { + buf.append('_'); + } + } + if (buf.length() == 0) { + buf.append('_'); + } + return buf.toString(); } } @@ -71,15 +81,53 @@ public abstract String format(String key); } - private static Pattern DEFAULT_CHARS = Pattern.compile("[.-]"); + /** + * Index into this array using a nibble, 4 bits, to get the corresponding + * hexadecimal character representation. + */ + private static final char NIBBLE_TO_HEX_CHAR[] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', + 'E', 'F'}; + + private static boolean needsUnicodeEscape(char ch) { + if (ch == ' ') { + return false; + } + switch (Character.getType(ch)) { + case Character.COMBINING_SPACING_MARK: + case Character.ENCLOSING_MARK: + case Character.NON_SPACING_MARK: + case Character.UNASSIGNED: + case Character.PRIVATE_USE: + case Character.SPACE_SEPARATOR: + case Character.CONTROL: + case Character.LINE_SEPARATOR: + case Character.FORMAT: + case Character.PARAGRAPH_SEPARATOR: + case Character.SURROGATE: + return true; + + default: + break; + } + return false; + } + + private static void unicodeEscape(char ch, StringBuilder buf) { + buf.append('\\'); + buf.append('u'); + buf.append(NIBBLE_TO_HEX_CHAR[(ch >> 12) & 0x0F]); + buf.append(NIBBLE_TO_HEX_CHAR[(ch >> 8) & 0x0F]); + buf.append(NIBBLE_TO_HEX_CHAR[(ch >> 4) & 0x0F]); + buf.append(NIBBLE_TO_HEX_CHAR[ch & 0x0F]); + } /** * Composer for the current Constant. */ protected SourceWriter composer; - private List<ResourceKeyFormatter> formatters = - new ArrayList<ResourceKeyFormatter>(); + private List<ResourceKeyFormatter> formatters = new ArrayList<ResourceKeyFormatter>(); private File resourceFile; @@ -147,7 +195,7 @@ * Create an annotation to hold the default value. */ protected abstract void genValueAnnotation(String defaultValue); - + /** * Returns the javaDocComment for the class. * @@ -156,24 +204,55 @@ */ protected abstract String javaDocComment(String path); - @SuppressWarnings("unchecked") // use of raw type from LocalizedProperties + protected String makeJavaString(String value) { + StringBuilder buf = new StringBuilder(); + buf.append('\"'); + for (int i = 0; i < value.length(); ++i) { + char c = value.charAt(i); + switch (c) { + case '\r': + buf.append("\\r"); + break; + case '\n': + buf.append("\\n"); + break; + case '\"': + buf.append("\\\""); + break; + default: + if (needsUnicodeEscape(c)) { + unicodeEscape(c, buf); + } else { + buf.append(c); + } + break; + } + } + buf.append('\"'); + return buf.toString(); + } + + @SuppressWarnings("unchecked") + // use of raw type from LocalizedProperties void generateFromPropertiesFile() throws IOException { InputStream propStream = new FileInputStream(resourceFile); LocalizedProperties p = new LocalizedProperties(); p.load(propStream, Util.DEFAULT_ENCODING); addFormatters(); // TODO: Look for a generic version of Tapestry's LocalizedProperties class - Iterator<Entry<String, String>> elements = - p.getPropertyMap().entrySet().iterator(); // suppress warnings - if (elements.hasNext() == false) { + Set<String> keySet = p.getPropertyMap().keySet(); + // sort keys for deterministic results + String[] keys = keySet.toArray(new String[keySet.size()]); + Arrays.sort(keys); + if (keys.length == 0) { throw new IllegalStateException( "File '" + resourceFile + "' cannot be used to generate message classes, as it has no key/value pairs defined."); } - while (elements.hasNext()) { - Entry<String, String> s = elements.next(); - genSimpleMethodDecl(s.getKey(), s.getValue()); + for (String key : keys) { + String value = p.getProperty(key); + genSimpleMethodDecl(key, value); } composer.commit(new PrintWriterTreeLogger()); } @@ -190,23 +269,19 @@ for (ResourceKeyFormatter formatter : formatters) { key = formatter.format(key); } - if (Util.isValidJavaIdent(key) == false) { - // TODO(jat): we could synthesize legal method names and add an - // @Key annotation to keep the matching key name. - throw new IllegalArgumentException(key - + " is not a legitimate method name."); - } return key; } private void genMethodDecl(String type, String defaultValue, String key) { composer.beginJavaDocComment(); - composer.println("Translated \"" + defaultValue + "\".\n"); - composer.println("@return translated \"" + defaultValue + "\""); + String escaped = makeJavaString(defaultValue); + composer.println("Translated " + escaped + ".\n"); + composer.print("@return translated " + escaped); composer.endJavaDocComment(); genValueAnnotation(defaultValue); - key = formatKey(key); - composer.print(type + " " + key); + composer.println("@Key(" + makeJavaString(key) + ")"); + String methodName = formatKey(key); + composer.print(type + " " + methodName); composer.print("("); genMethodArgs(defaultValue); composer.print(");\n");
diff --git a/user/src/com/google/gwt/i18n/rebind/AnnotationUtil.java b/user/src/com/google/gwt/i18n/rebind/AnnotationUtil.java new file mode 100644 index 0000000..21fbbd6 --- /dev/null +++ b/user/src/com/google/gwt/i18n/rebind/AnnotationUtil.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.i18n.rebind; + +import com.google.gwt.core.ext.typeinfo.JClassType; + +import java.lang.annotation.Annotation; + +/** + * Utility class for i18n-related annotation manipulation routines. + */ +public class AnnotationUtil { + + /** + * Find an instance of the specified annotation, walking up the inheritance + * tree if necessary. + * + * <p>Note that i18n annotations may appear on classes as well as interfaces + * (a concrete implementation can be supplied rather than just an interface + * and this is the normal way of using generic Localizable interfaces), so + * we have to search the super chain as well as other interfaces. + * + * <p>The super chain is walked first, so if an ancestor superclass has the + * requested annotation, it will be preferred over a directly implemented + * interface. + * + * @param <T> Annotation type to search for + * @param clazz root class to search, may be null + * @param annotationClass class object of Annotation subclass to search for + * @return the requested annotation or null if none + */ + static <T extends Annotation> T getClassAnnotation(JClassType clazz, + Class<T> annotationClass) { + if (clazz == null) { + return null; + } + T annot = clazz.getAnnotation(annotationClass); + if (annot == null) { + annot = getClassAnnotation(clazz.getSuperclass(), annotationClass); + if (annot != null) { + return annot; + } + for (JClassType intf : clazz.getImplementedInterfaces()) { + annot = getClassAnnotation(intf, annotationClass); + if (annot != null) { + return annot; + } + } + } + return annot; + } + +}
diff --git a/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java b/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java index 87b9d05..9e5cc4e 100644 --- a/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java +++ b/user/src/com/google/gwt/i18n/rebind/AnnotationsResource.java
@@ -13,9 +13,10 @@ * License for the specific language governing permissions and limitations under * the License. */ - package com.google.gwt.i18n.rebind; +import static com.google.gwt.i18n.rebind.AnnotationUtil.getClassAnnotation; + import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.typeinfo.JArrayType; import com.google.gwt.core.ext.typeinfo.JClassType; @@ -167,7 +168,7 @@ */ public static KeyGenerator getKeyGenerator(JClassType targetClass) throws AnnotationsError { - GenerateKeys generator = targetClass.getAnnotation(GenerateKeys.class); + GenerateKeys generator = getClassAnnotation(targetClass, GenerateKeys.class); if (generator != null) { String className = generator.value(); try { @@ -377,7 +378,7 @@ KeyGenerator keyGenerator = getKeyGenerator(clazz); map = new HashMap<String, MethodEntry>(); setPath(clazz.getQualifiedSourceName()); - DefaultLocale defLocale = clazz.getAnnotation(DefaultLocale.class); + DefaultLocale defLocale = getClassAnnotation(clazz, DefaultLocale.class); if (defLocale != null && !ResourceFactory.DEFAULT_TOKEN.equals(locale) && !locale.equalsIgnoreCase(defLocale.value())) { logger.log(TreeLogger.WARN, "@DefaultLocale on "
diff --git a/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java b/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java index ef7bcd9..de7685b 100644 --- a/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/ConstantsInterfaceCreator.java
@@ -50,13 +50,13 @@ @Override protected void genValueAnnotation(String defaultValue) { - composer.println("@DefaultStringValue(\"" + defaultValue.replace("\"", "\\\"") - + "\")"); + composer.println("@DefaultStringValue(" + makeJavaString(defaultValue) + + ")"); } @Override protected String javaDocComment(String path) { - return "Interface to represent the constants contained in resource bundle:\n\t'" + return "Interface to represent the constants contained in resource bundle:\n\t'" + path + "'."; } }
diff --git a/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java b/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java index 84ee86a..9c84209 100644 --- a/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java +++ b/user/src/com/google/gwt/i18n/rebind/MessagesInterfaceCreator.java
@@ -92,8 +92,7 @@ @Override protected void genValueAnnotation(String defaultValue) { - composer.println("@DefaultMessage(\"" + defaultValue.replace("\"", "\\\"") - + "\")"); + composer.println("@DefaultMessage(" + makeJavaString(defaultValue) + ")"); } @Override
diff --git a/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java b/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java index ee5f501..f404f24 100644 --- a/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java +++ b/user/src/com/google/gwt/i18n/rebind/ResourceFactory.java
@@ -304,8 +304,15 @@ String partialPath = localizedPath.replace('.', '/'); for (int i = 0; i < loaders.size(); i++) { ResourceFactory element = loaders.get(i); - String path = partialPath + "." + element.getExt(); + String ext = "." + element.getExt(); + String path = partialPath + ext; InputStream m = loader.getResourceAsStream(path); + if (m == null && partialPath.contains("$")) { + // Also look for A_B for inner classes, as $ in path names + // can cause issues for some build tools. + path = partialPath.replace('$', '_') + ext; + m = loader.getResourceAsStream(path); + } if (m != null) { AbstractResource found = element.load(m); found.setPath(path);
diff --git a/user/src/com/google/gwt/i18n/tools/I18NSync.java b/user/src/com/google/gwt/i18n/tools/I18NSync.java index 18da760..06bed9a 100644 --- a/user/src/com/google/gwt/i18n/tools/I18NSync.java +++ b/user/src/com/google/gwt/i18n/tools/I18NSync.java
@@ -337,7 +337,11 @@ + "'should not contain an extension. \"com.google.gwt.SomeClass\" is an example of a correctly formed class string"); } String resourcePath = className.replace('.', '/') + ".properties"; - URL r = ClassLoader.getSystemResource(resourcePath); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + URL r = cl.getResource(resourcePath); if (r == null) { throw new FileNotFoundException("Could not find the resource '" + resourcePath + " matching '" + className
diff --git a/user/src/com/google/gwt/junit/JUnitShell.java b/user/src/com/google/gwt/junit/JUnitShell.java index d485cc5..b2ed7605 100644 --- a/user/src/com/google/gwt/junit/JUnitShell.java +++ b/user/src/com/google/gwt/junit/JUnitShell.java
@@ -511,7 +511,7 @@ protected void initializeLogger() { if (isHeadless()) { consoleLogger = new PrintWriterTreeLogger(); - consoleLogger.setMaxDetail(getLogLevel()); + consoleLogger.setMaxDetail(getCompilerOptions().getLogLevel()); } else { super.initializeLogger(); }
diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/TreeMap_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/TreeMap_CustomFieldSerializer.java new file mode 100644 index 0000000..ddc24f2 --- /dev/null +++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/TreeMap_CustomFieldSerializer.java
@@ -0,0 +1,47 @@ +/* + * 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.user.client.rpc.core.java.util; + +import com.google.gwt.user.client.rpc.SerializationException; +import com.google.gwt.user.client.rpc.SerializationStreamReader; +import com.google.gwt.user.client.rpc.SerializationStreamWriter; + +import java.util.Comparator; +import java.util.TreeMap; + +/** + * Custom field serializer for {@link java.util.TreeMap}. + */ +@SuppressWarnings("unchecked") +public class TreeMap_CustomFieldSerializer { + + /* for now, build it entry by entry. Can optimize later via bulk loading */ + public static void deserialize(SerializationStreamReader streamReader, + TreeMap instance) throws SerializationException { + Map_CustomFieldSerializerBase.deserialize(streamReader, instance); + } + + public static TreeMap instantiate(SerializationStreamReader streamReader) + throws SerializationException { + return new TreeMap((Comparator) streamReader.readObject()); + } + + public static void serialize(SerializationStreamWriter streamWriter, + TreeMap instance) throws SerializationException { + streamWriter.writeObject(instance.comparator()); + Map_CustomFieldSerializerBase.serialize(streamWriter, instance); + } +}
diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/util/TreeSet_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/util/TreeSet_CustomFieldSerializer.java new file mode 100644 index 0000000..f48f95e --- /dev/null +++ b/user/src/com/google/gwt/user/client/rpc/core/java/util/TreeSet_CustomFieldSerializer.java
@@ -0,0 +1,47 @@ +/* + * 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.user.client.rpc.core.java.util; + +import com.google.gwt.user.client.rpc.SerializationException; +import com.google.gwt.user.client.rpc.SerializationStreamReader; +import com.google.gwt.user.client.rpc.SerializationStreamWriter; + +import java.util.Comparator; +import java.util.TreeSet; + +/** + * Custom field serializer for {@link java.util.TreeMap}. + */ +@SuppressWarnings("unchecked") +public class TreeSet_CustomFieldSerializer { + + /* for now, build it entry by entry. Can optimize later via bulk loading */ + public static void deserialize(SerializationStreamReader streamReader, + TreeSet instance) throws SerializationException { + Collection_CustomFieldSerializerBase.deserialize(streamReader, instance); + } + + public static TreeSet instantiate(SerializationStreamReader streamReader) + throws SerializationException { + return new TreeSet((Comparator) streamReader.readObject()); + } + + public static void serialize(SerializationStreamWriter streamWriter, + TreeSet instance) throws SerializationException { + streamWriter.writeObject(instance.comparator()); + Collection_CustomFieldSerializerBase.serialize(streamWriter, instance); + } +}
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java index 51c4483..2177020 100644 --- a/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java +++ b/user/src/com/google/gwt/user/client/rpc/impl/AbstractSerializationStream.java
@@ -23,9 +23,21 @@ public abstract class AbstractSerializationStream { /** + * The character used to separate fields in client->server RPC messages. + * + * Note that this character is referenced in the following places not using + * this constant, and they must be changed if this is: + * <ul> + * <li>{@link ServerSerializationStreamWriter}.deserializeStringTable + * <li>{@link ClientSerializationStreamReader}.getQuotingRegex + * </ul> + */ + public static final char RPC_SEPARATOR_CHAR = '|'; + + /** * This is the only supported RPC protocol version. */ - public static final int SERIALIZATION_STREAM_VERSION = 4; + public static final int SERIALIZATION_STREAM_VERSION = 5; private int flags = 0; private int version = SERIALIZATION_STREAM_VERSION;
diff --git a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java index f44d1ec..f760109 100644 --- a/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java +++ b/user/src/com/google/gwt/user/client/rpc/impl/ClientSerializationStreamWriter.java
@@ -16,6 +16,7 @@ package com.google.gwt.user.client.rpc.impl; import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.UnsafeNativeLong; import com.google.gwt.user.client.rpc.SerializationException; @@ -27,18 +28,97 @@ public final class ClientSerializationStreamWriter extends AbstractSerializationStreamWriter { + /** + * Used by JSNI, see {@link #quoteString(String)}. + */ + @SuppressWarnings("unused") + private static JavaScriptObject regex = getQuotingRegex(); + private static void append(StringBuffer sb, String token) { assert (token != null); sb.append(token); - sb.append('\uffff'); + sb.append(RPC_SEPARATOR_CHAR); } + /** + * Create the RegExp instance used for quoting dangerous characters in user + * payload strings. + * + * Note that {@link AbstractSerializationStream#RPC_SEPARATOR_CHAR} is used in + * this expression, which must be updated if the separator character is + * changed. + * + * For Android WebKit, we quote many more characters to keep them from being + * mangled. + * + * @return RegExp object + */ + private static native JavaScriptObject getQuotingRegex() /*-{ + // "|" = AbstractSerializationStream.RPC_SEPARATOR_CHAR + var ua = navigator.userAgent.toLowerCase(); + var webkitregex = /webkit\/([\d]+)/; + var webkit = 0; + var result = webkitregex.exec(ua); + if (result) { + webkit = parseInt(result[1]); + } + if (ua.indexOf("android") != -1) { + // initial version of Android WebKit has a double-encoding bug for UTF8, + // so we have to encode every non-ASCII character. + // TODO(jat): revisit when this bug is fixed in Android + return /[\u0000\|\\\u0080-\uFFFF]/g; + } else if (webkit < 522) { + // Safari 2 doesn't handle \\uXXXX in regexes + // TODO(jat): should iPhone be treated specially? + return /[\x00\|\\]/g; + } else if (webkit > 0) { + // other WebKit-based browsers need some additional quoting + return /[\u0000\|\\\u0300-\u036F\u0590-\u05FF\uD800-\uFFFF]/g; + } else { + return /[\u0000\|\\\uD800-\uFFFF]/g; + } + }-*/; + @UnsafeNativeLong // Keep synchronized with LongLib private static native double[] makeLongComponents0(long value) /*-{ return value; }-*/; + /** + * Quote characters in a user-supplied string to make sure they are safe to + * send to the server. + * + * See {@link ServerSerializationStreamReader#deserializeStringTable} for the + * corresponding dequoting. + * + * @param str string to quote + * @return quoted string + */ + private static native String quoteString(String str) /*-{ + var regex = @com.google.gwt.user.client.rpc.impl.ClientSerializationStreamWriter::regex; + var idx = 0; + var out = ""; + var result; + while ((result = regex.exec(str)) != null) { + out += str.substring(idx, result.index); + idx = result.index + 1; + var ch = result[0].charCodeAt(0); + if (ch == 0) { + out += "\\0"; + } else if (ch == 92) { // backslash + out += "\\\\"; + } else if (ch == 124) { // vertical bar + // 124 = "|" = AbstractSerializationStream.RPC_SEPARATOR_CHAR + out += "\\!"; + } else { + var hex = ch.toString(16); + out += "\\u0000".substring(0, 6 - hex.length) + hex; + } + } + return out + str.substring(idx); + }-*/; + private StringBuffer encodeBuffer; private final String moduleBaseURL; @@ -67,6 +147,7 @@ * Call this method before attempting to append any tokens. This method * implementation <b>must</b> be called by any overridden version. */ + @Override public void prepareToWrite() { super.prepareToWrite(); encodeBuffer = new StringBuffer(); @@ -148,7 +229,7 @@ List<String> stringTable = getStringTable(); append(buffer, String.valueOf(stringTable.size())); for (String s : stringTable) { - append(buffer, s); + append(buffer, quoteString(s)); } return buffer; }
diff --git a/user/src/com/google/gwt/user/client/ui/SuggestBox.java b/user/src/com/google/gwt/user/client/ui/SuggestBox.java index 179f4ec..e4ba0ee 100644 --- a/user/src/com/google/gwt/user/client/ui/SuggestBox.java +++ b/user/src/com/google/gwt/user/client/ui/SuggestBox.java
@@ -15,6 +15,7 @@ */ package com.google.gwt.user.client.ui; +import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Window; @@ -23,7 +24,6 @@ import com.google.gwt.user.client.ui.SuggestOracle.Request; import com.google.gwt.user.client.ui.SuggestOracle.Response; import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; -import com.google.gwt.i18n.client.LocaleInfo; import java.util.ArrayList; import java.util.Collection; @@ -370,6 +370,7 @@ private static final String STYLENAME_DEFAULT = "gwt-SuggestBox"; private int limit = 20; + private boolean selectsFirstItem = false; private SuggestOracle oracle; private String currentText; private final SuggestionMenu suggestionMenu; @@ -500,6 +501,16 @@ } /** + * Returns whether or not the first suggestion will be automatically + * selected. This behavior is off by default. + * + * @return true if the first suggestion will be automatically selected + */ + public boolean getSelectsFirstItem() { + return selectsFirstItem; + } + + /** * Gets the suggest box's {@link com.google.gwt.user.client.ui.SuggestOracle}. * * @return the {@link SuggestOracle} @@ -582,7 +593,18 @@ public void setPopupStyleName(String style) { suggestionPopup.setStyleName(style); } - + + /** + * Turns on or off the behavior that automatically selects the first suggested + * item. It defaults to off. + * + * @param selectsFirstItem Whether or not to automatically select the first + * suggested + */ + public void setSelectsFirstItem(boolean selectsFirstItem) { + this.selectsFirstItem = selectsFirstItem; + } + public void setTabIndex(int index) { box.setTabIndex(index); } @@ -638,8 +660,10 @@ suggestionMenu.addItem(menuItem); } - // Select the first item in the suggestion menu. - suggestionMenu.selectItem(0); + if (selectsFirstItem) { + // Select the first item in the suggestion menu. + suggestionMenu.selectItem(0); + } suggestionPopup.showAlignedPopup(); suggestionPopup.setAnimationEnabled(isAnimationEnabled); @@ -665,7 +689,11 @@ break; case KeyboardListener.KEY_ENTER: case KeyboardListener.KEY_TAB: - suggestionMenu.doSelectedItemAction(); + if (suggestionMenu.getSelectedItemIndex() < 0) { + suggestionPopup.hide(); + } else { + suggestionMenu.doSelectedItemAction(); + } break; } }
diff --git a/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java b/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java index 658c1a6..034949c 100644 --- a/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java +++ b/user/src/com/google/gwt/user/rebind/ClassSourceFileComposer.java
@@ -57,22 +57,24 @@ throw new IllegalArgumentException("Cannot supply a null package name to" + targetClassShortName); } - // Inlined header to only have one method with a huge number of methods. + // TODO: support a user-specified file header if (targetPackageName.length() > 0) { println("package " + targetPackageName + ";"); } - println(); if (imports != null && imports.length > 0) { + println(); for (int i = 0, n = imports.length; i < n; ++i) { println("import " + imports[i] + ";"); } - println(); } if (classJavaDocComment != null) { beginJavaDocComment(); print(classJavaDocComment); endJavaDocComment(); + } else { + // beginJavaDocComment adds its own leading newline, make up for it here. + println(); } if (category == JavaSourceCategory.CLASS) { emitClassDecl(targetClassShortName, superClassName, interfaceNames);
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java index 3e1d87e..92e1cd3 100644 --- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java +++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamReader.java
@@ -77,43 +77,50 @@ private enum ValueReader { BOOLEAN { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readBoolean(); } }, BYTE { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readByte(); } }, CHAR { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readChar(); } }, DOUBLE { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readDouble(); } }, FLOAT { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readFloat(); } }, INT { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readInt(); } }, LONG { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readLong(); } }, @@ -126,13 +133,15 @@ }, SHORT { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readShort(); } }, STRING { @Override - Object readValue(ServerSerializationStreamReader stream) { + Object readValue(ServerSerializationStreamReader stream) + throws SerializationException { return stream.readString(); } }; @@ -323,6 +332,7 @@ private final ArrayList<String> tokenList = new ArrayList<String>(); private int tokenListIndex; + { CLASS_TO_VECTOR_READER.put(boolean[].class, VectorReader.BOOLEAN_VECTOR); CLASS_TO_VECTOR_READER.put(byte[].class, VectorReader.BYTE_VECTOR); @@ -374,11 +384,30 @@ stringTable = null; int idx = 0, nextIdx; - while (-1 != (nextIdx = encodedTokens.indexOf('\uffff', idx))) { + while (-1 != (nextIdx = encodedTokens.indexOf(RPC_SEPARATOR_CHAR, idx))) { String current = encodedTokens.substring(idx, nextIdx); tokenList.add(current); idx = nextIdx + 1; } + if (idx == 0) { + // Didn't find any separator, assume an older version with different + // separators and get the version as the sequence of digits at the + // beginning of the encoded string. + while (idx < encodedTokens.length() + && Character.isDigit(encodedTokens.charAt(idx))) { + ++idx; + } + if (idx == 0) { + throw new IncompatibleRemoteServiceException( + "Malformed or old RPC message received - expecting version " + + SERIALIZATION_STREAM_VERSION); + } else { + int version = Integer.valueOf(encodedTokens.substring(0, idx)); + throw new IncompatibleRemoteServiceException("Expecting version " + + SERIALIZATION_STREAM_VERSION + " from client, got " + version + + "."); + } + } super.prepareToRead(encodedTokens); @@ -407,42 +436,42 @@ } } - public boolean readBoolean() { + public boolean readBoolean() throws SerializationException { return !extract().equals("0"); } - public byte readByte() { + public byte readByte() throws SerializationException { return Byte.parseByte(extract()); } - public char readChar() { + public char readChar() throws SerializationException { // just use an int, it's more foolproof return (char) Integer.parseInt(extract()); } - public double readDouble() { + public double readDouble() throws SerializationException { return Double.parseDouble(extract()); } - public float readFloat() { + public float readFloat() throws SerializationException { return (float) Double.parseDouble(extract()); } - public int readInt() { + public int readInt() throws SerializationException { return Integer.parseInt(extract()); } - public long readLong() { + public long readLong() throws SerializationException { // Keep synchronized with LongLib. The wire format are the two component // parts of the double in the client code. return (long) readDouble() + (long) readDouble(); } - public short readShort() { + public short readShort() throws SerializationException { return Short.parseShort(extract()); } - public String readString() { + public String readString() throws SerializationException { return getString(readInt()); } @@ -587,7 +616,50 @@ BoundedList<String> buffer = new BoundedList<String>(String.class, typeNameCount); for (int typeNameIndex = 0; typeNameIndex < typeNameCount; ++typeNameIndex) { - buffer.add(extract()); + String str = extract(); + // Change quoted characters back. + int idx = str.indexOf('\\'); + if (idx >= 0) { + StringBuilder buf = new StringBuilder(); + int pos = 0; + while (idx >= 0) { + buf.append(str.substring(pos, idx)); + if (++idx == str.length()) { + throw new SerializationException("Unmatched backslash: \"" + + str + "\""); + } + char ch = str.charAt(idx); + pos = idx + 1; + switch (ch) { + case '0': + buf.append('\u0000'); + break; + case '!': + buf.append(RPC_SEPARATOR_CHAR); + break; + case '\\': + buf.append(ch); + break; + case 'u': + try { + ch = (char) Integer.parseInt(str.substring(idx + 1, idx + 5), 16); + } catch (NumberFormatException e) { + throw new SerializationException( + "Invalid Unicode escape sequence in \"" + str + "\""); + } + buf.append(ch); + pos += 4; + break; + default: + throw new SerializationException("Unexpected escape character " + + ch + " after backslash: \"" + str + "\""); + } + idx = str.indexOf('\\', pos); + } + buf.append(str.substring(pos)); + str = buf.toString(); + } + buffer.add(str); } if (buffer.size() != buffer.getExpectedSize()) { @@ -613,14 +685,18 @@ throw new NoSuchMethodException("deserialize"); } - private String extract() { - return tokenList.get(tokenListIndex++); + private String extract() throws SerializationException { + try { + return tokenList.get(tokenListIndex++); + } catch (IndexOutOfBoundsException e) { + throw new SerializationException("Too few tokens in RPC request", e); + } } private Object instantiate(Class<?> customSerializer, Class<?> instanceClass) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, - NoSuchMethodException { + NoSuchMethodException, SerializationException { if (customSerializer != null) { for (Method method : customSerializer.getMethods()) { if ("instantiate".equals(method.getName())) {
diff --git a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java index f355f3c..8f837cd 100644 --- a/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java +++ b/user/src/com/google/gwt/user/server/rpc/impl/ServerSerializationStreamWriter.java
@@ -371,11 +371,7 @@ for (int i = 0, n = input.length; i < n; ++i) { char c = input[i]; - if (c < NUMBER_OF_JS_ESCAPED_CHARS && JS_CHARS_ESCAPED[c] != 0) { - charVector.add(JS_ESCAPE_CHAR); - charVector.add(JS_CHARS_ESCAPED[c]); - } else if (needsUnicodeEscape(c)) { - charVector.add(JS_ESCAPE_CHAR); + if (needsUnicodeEscape(c)) { unicodeEscape(c, charVector); } else { charVector.add(c); @@ -444,43 +440,57 @@ * </ol> */ private static boolean needsUnicodeEscape(char ch) { - switch (Character.getType(ch)) { - // Conservative - case Character.COMBINING_SPACING_MARK: - case Character.ENCLOSING_MARK: - case Character.NON_SPACING_MARK: - case Character.UNASSIGNED: - case Character.PRIVATE_USE: - case Character.SPACE_SEPARATOR: - case Character.CONTROL: - - // Minimal - case Character.LINE_SEPARATOR: - case Character.FORMAT: - case Character.PARAGRAPH_SEPARATOR: - case Character.SURROGATE: + switch (ch) { + case ' ': + // ASCII space gets caught in SPACE_SEPARATOR below, but does not + // need to be escaped + return false; + case JS_QUOTE_CHAR: + case JS_ESCAPE_CHAR: + // these must be quoted or they will break the protocol return true; - - default: - if (ch == NON_BREAKING_HYPHEN) { + case NON_BREAKING_HYPHEN: // This can be expanded into a break followed by a hyphen return true; + default: + switch (Character.getType(ch)) { + // Conservative + case Character.COMBINING_SPACING_MARK: + case Character.ENCLOSING_MARK: + case Character.NON_SPACING_MARK: + case Character.UNASSIGNED: + case Character.PRIVATE_USE: + case Character.SPACE_SEPARATOR: + case Character.CONTROL: + + // Minimal + case Character.LINE_SEPARATOR: + case Character.FORMAT: + case Character.PARAGRAPH_SEPARATOR: + case Character.SURROGATE: + return true; + + default: + break; } break; } - return false; } /** - * Writes either the two or four character escape sequence for a character. - * + * Writes a safe escape sequence for a character. Some characters have a + * short form, such as \n for U+000D, while others are represented as \\xNN + * or \\uNNNN. * * @param ch character to unicode escape * @param charVector char vector to receive the unicode escaped representation */ private static void unicodeEscape(char ch, CharVector charVector) { - if (ch < 256) { + charVector.add(JS_ESCAPE_CHAR); + if (ch < NUMBER_OF_JS_ESCAPED_CHARS && JS_CHARS_ESCAPED[ch] != 0) { + charVector.add(JS_CHARS_ESCAPED[ch]); + } else if (ch < 256) { charVector.add('x'); charVector.add(NIBBLE_TO_HEX_CHAR[(ch >> 4) & 0x0F]); charVector.add(NIBBLE_TO_HEX_CHAR[ch & 0x0F]);
diff --git a/user/super/com/google/gwt/emul/java/io/IOException.java b/user/super/com/google/gwt/emul/java/io/IOException.java new file mode 100644 index 0000000..34362a6 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/io/IOException.java
@@ -0,0 +1,40 @@ +/* + * 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 java.io; + +/** + * See <a + * href="http://java.sun.com/javase/6/docs/api/java/io/IOException.html">the + * official Java API doc</a> for details. + */ +public class IOException extends Exception { + + public IOException() { + super(); + } + + public IOException(String message) { + super(message); + } + + public IOException(String message, Throwable throwable) { + super(message, throwable); + } + + public IOException(Throwable throwable) { + super(throwable); + } +}
diff --git a/user/super/com/google/gwt/emul/java/lang/Appendable.java b/user/super/com/google/gwt/emul/java/lang/Appendable.java new file mode 100644 index 0000000..c0c4c85 --- /dev/null +++ b/user/super/com/google/gwt/emul/java/lang/Appendable.java
@@ -0,0 +1,33 @@ +/* + * 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 java.lang; + +import java.io.IOException; + +/** + * See <a + * href="http://java.sun.com/javase/6/docs/api/java/lang/Appendable.html">the + * official Java API doc</a> for details. + */ +public interface Appendable { + + Appendable append(char c) throws IOException; + + Appendable append(CharSequence charSquence) throws IOException; + + Appendable append(CharSequence charSquence, int start, int end) + throws IOException; +}
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java index f851bef..2b24557 100644 --- a/user/super/com/google/gwt/emul/java/lang/StringBuffer.java +++ b/user/super/com/google/gwt/emul/java/lang/StringBuffer.java
@@ -30,7 +30,7 @@ * This class is an exact clone of {@link StringBuilder} except for the name. * Any change made to one should be mirrored in the other. */ -public class StringBuffer implements CharSequence { +public class StringBuffer implements CharSequence, Appendable { private final StringBufferImpl impl = GWT.create(StringBufferImpl.class); private final Object data = impl.createData();
diff --git a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java index 00f9d19..e6d495a 100644 --- a/user/super/com/google/gwt/emul/java/lang/StringBuilder.java +++ b/user/super/com/google/gwt/emul/java/lang/StringBuilder.java
@@ -30,7 +30,7 @@ * This class is an exact clone of {@link StringBuffer} except for the name. Any * change made to one should be mirrored in the other. */ -public class StringBuilder implements CharSequence { +public class StringBuilder implements CharSequence, Appendable { private final StringBufferImpl impl = GWT.create(StringBufferImpl.class); private final Object data = impl.createData();
diff --git a/user/super/com/google/gwt/emul/java/sql/Time.java b/user/super/com/google/gwt/emul/java/sql/Time.java index a3302af..81c81cd 100644 --- a/user/super/com/google/gwt/emul/java/sql/Time.java +++ b/user/super/com/google/gwt/emul/java/sql/Time.java
@@ -27,9 +27,9 @@ } try { - int hh = Integer.decode(split[0]); - int mm = Integer.decode(split[1]); - int ss = Integer.decode(split[2]); + int hh = Integer.parseInt(split[0]); + int mm = Integer.parseInt(split[1]); + int ss = Integer.parseInt(split[2]); return new Time(hh, mm, ss); } catch (NumberFormatException e) {
diff --git a/user/super/com/google/gwt/emul/java/util/TreeMap.java b/user/super/com/google/gwt/emul/java/util/TreeMap.java index 2edb562..1c6ffcd 100644 --- a/user/super/com/google/gwt/emul/java/util/TreeMap.java +++ b/user/super/com/google/gwt/emul/java/util/TreeMap.java
@@ -15,6 +15,8 @@ */ package java.util; +import java.io.Serializable; + /** * Implements a TreeMap using a red-black tree. This guarantees O(log n) * performance on lookups, inserts, and deletes while maintaining linear @@ -24,7 +26,8 @@ * @param <K> key type * @param <V> value type */ -public class TreeMap<K, V> extends AbstractMap<K, V> implements SortedMap<K, V> { +public class TreeMap<K, V> extends AbstractMap<K, V> implements + SortedMap<K, V>, Serializable { /* * Implementation derived from public domain C implementation as of 5 * September 2007 at: @@ -32,8 +35,6 @@ * written by Julienne Walker. * * This version does not require a parent pointer kept in each node. - * - * TODO: should this class be serializable? What to do about the comparator? */ /** @@ -494,14 +495,14 @@ private enum SubMapType { All, - + Head { @Override public boolean toKeyValid() { return true; } }, - + Range { @Override public boolean fromKeyValid() { @@ -513,21 +514,21 @@ return true; } }, - + Tail { @Override public boolean fromKeyValid() { return true; } }; - + /** * @return true if this submap type uses a from-key. */ public boolean fromKeyValid() { return false; } - + /** * @return true if this submap type uses a to-key. */ @@ -580,8 +581,17 @@ // The comparator to use. private Comparator<? super K> cmp; + /* + * These two fields are just hints to STOB so that it generates serializers + * for K and V + */ + @SuppressWarnings("unused") + private K exposeKeyType; + @SuppressWarnings("unused") + private V exposeValueType; + // The root of the tree. - private Node<K, V> root; + private transient Node<K, V> root; // The number of nodes in the tree. private int size = 0; @@ -843,14 +853,13 @@ /** * Insert a node into a subtree, collecting state about the insertion. * - * If the same key already exists, the value of the node is overwritten - * with the value from the new node instead. + * If the same key already exists, the value of the node is overwritten with + * the value from the new node instead. * * @param tree subtree to insert into * @param newNode new node to insert - * @param state result of the insertion: - * state.found true if the key already existed in the tree - * state.value the old value if the key existed + * @param state result of the insertion: state.found true if the key already + * existed in the tree state.value the old value if the key existed * @return the new subtree root */ private Node<K, V> insert(Node<K, V> tree, Node<K, V> newNode, State<V> state) {
diff --git a/user/super/com/google/gwt/emul/java/util/TreeSet.java b/user/super/com/google/gwt/emul/java/util/TreeSet.java index b4bce7f..496e87d 100644 --- a/user/super/com/google/gwt/emul/java/util/TreeSet.java +++ b/user/super/com/google/gwt/emul/java/util/TreeSet.java
@@ -15,6 +15,8 @@ */ package java.util; +import java.io.Serializable; + /** * Implements a set using a TreeMap. <a * href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/TreeSet.html">[Sun @@ -22,15 +24,15 @@ * * @param <E> element type. */ -public class TreeSet<E> extends AbstractSet<E> implements SortedSet<E> { +public class TreeSet<E> extends AbstractSet<E> implements SortedSet<E>, Serializable { /** - * TreeSet is stored as a TreeMap of the requested type to null Objects. + * TreeSet is stored as a TreeMap of the requested type to a constant integer. */ - private SortedMap<E, Object> map; + SortedMap<E, Boolean> map; public TreeSet() { - map = new TreeMap<E, Object>(); + map = new TreeMap<E, Boolean>(); } public TreeSet(Collection<? extends E> c) { @@ -40,9 +42,9 @@ public TreeSet(Comparator<? super E> c) { if (c == null) { - map = new TreeMap<E, Object>(); + map = new TreeMap<E, Boolean>(); } else { - map = new TreeMap<E, Object>(c); + map = new TreeMap<E, Boolean>(c); } } @@ -57,14 +59,14 @@ * * @param map map to use for backing store */ - private TreeSet(SortedMap<E, Object> map) { + private TreeSet(SortedMap<E, Boolean> map) { this.map = map; } @Override public boolean add(E o) { // Use "this" as a convenient non-null value to store in the map - return map.put(o, this) == null; + return map.put(o, Boolean.FALSE) == null; } @Override
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java index 4c744e7..e6e3657 100644 --- a/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java +++ b/user/test/com/google/gwt/dev/jjs/test/CompilerTest.java
@@ -26,9 +26,29 @@ @SuppressWarnings("unused") public class CompilerTest extends GWTTestCase { + private abstract static class AbstractSuper { + public static String foo() { + if (FALSE) { + // prevent inlining + return foo(); + } + return "AbstractSuper"; + } + } + private abstract static class Apple implements Fruit { } + private static class ConcreteSub extends AbstractSuper { + public static String foo() { + if (FALSE) { + // prevent inlining + return foo(); + } + return "ConcreteSub"; + } + } + private static interface Fruit { } @@ -174,6 +194,8 @@ return @com.google.gwt.dev.jjs.test.CompilerTest$SideEffectCauser5::causeClinitSideEffectOnRead; }-*/; + private Integer boxedInteger = 0; + @Override public String getModuleName() { return "com.google.gwt.dev.jjs.CompilerSuite"; @@ -557,6 +579,16 @@ assertEquals("null true", test); } + /** + * Issue 2886: inlining should cope with local variables that do not have an + * explicit declaration node. + */ + public void testInliningBoxedIncrement() { + // should not actually inline, because it has a temp variable + incrementBoxedInteger(); + assertEquals((Integer) 1, boxedInteger); + } + public void testJavaScriptReservedWords() { boolean delete = TRUE; for (int in = 0; in < 10; ++in) { @@ -777,6 +809,11 @@ assertEquals(new Foo(2).i, 2); } + public void testStaticMethodResolution() { + // Issue 2922 + assertEquals("AbstractSuper", AbstractSuper.foo()); + } + public void testStringOptimizations() { assertEquals("Herro, AJAX", "Hello, AJAX".replace('l', 'r')); assertEquals('J', "Hello, AJAX".charAt(8)); @@ -988,6 +1025,11 @@ } } + private void incrementBoxedInteger() { + // the following will need a temporary variable created + boxedInteger++; + } + private boolean returnFalse() { return false; }
diff --git a/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java b/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java index e3129f4..0646e3c 100644 --- a/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java +++ b/user/test/com/google/gwt/emultest/java/sql/SqlTimeTest.java
@@ -102,13 +102,20 @@ } Time t = Time.valueOf("13:01:30"); - // Months are 0-based, days are 1-based assertEquals(13, t.getHours()); assertEquals(1, t.getMinutes()); assertEquals(30, t.getSeconds()); Time d2 = Time.valueOf(t.toString()); assertEquals(t, d2); + + // tests to see if the various parts are indeed decoded in base-10 (till + // r3728 the base was first inferred) + Time t2 = Time.valueOf("08:09:01"); + assertEquals(8, t2.getHours()); + assertEquals(9, t2.getMinutes()); + assertEquals(1, t2.getSeconds()); + assertEquals(t2, Time.valueOf(t2.toString())); } public void testToString() {
diff --git a/user/test/com/google/gwt/i18n/client/CommonInterfaceAnnotations.java b/user/test/com/google/gwt/i18n/client/CommonInterfaceAnnotations.java new file mode 100644 index 0000000..c33704d --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/CommonInterfaceAnnotations.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.i18n.client; + +import com.google.gwt.i18n.client.LocalizableResource.GenerateKeys; + +/** + * Base interface to test annotation inheritance. + * + * <p>This works by setting the key generator to MD5 on this interface, + * then verifying that keys in the subinterface are looked up with + * MD5 hashes rather than method names. + */ +@GenerateKeys("com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator") +public interface CommonInterfaceAnnotations extends Messages { + + @DefaultMessage("foo") + String foo(); +}
diff --git a/user/test/com/google/gwt/i18n/client/I18N2Test.java b/user/test/com/google/gwt/i18n/client/I18N2Test.java index 3cc843b..89568ab 100644 --- a/user/test/com/google/gwt/i18n/client/I18N2Test.java +++ b/user/test/com/google/gwt/i18n/client/I18N2Test.java
@@ -16,6 +16,7 @@ package com.google.gwt.i18n.client; import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.TestAnnotatedMessages.Nested; import com.google.gwt.i18n.client.gen.Colors; import com.google.gwt.i18n.client.gen.TestBadKeys; import com.google.gwt.junit.client.GWTTestCase; @@ -32,6 +33,7 @@ return "com.google.gwt.i18n.I18N2Test"; } + @SuppressWarnings("deprecation") public void testAnnotatedMessages() { TestAnnotatedMessages m = GWT.create(TestAnnotatedMessages.class); assertEquals("Test me", m.basicText()); @@ -103,6 +105,10 @@ assertEquals("a_b_c", test.a_b_c()); assertEquals("a_b_c", test.getString("a_b_c")); assertEquals("__s_dup_dup", test.__s_dup_dup()); + assertEquals("e in b_C_d", test.getString("__dup_dup")); + assertEquals("e in b_C_d", test.__dup_dup()); + assertEquals("andStar", test.getString("__")); + assertEquals("andStar", test.__()); } public void testBinding() { @@ -125,6 +131,18 @@ assertEquals("a circle", s.circle()); } + /** + * Verify that nested annotations are looked up with both A$B names + * and A_B names. Note that $ takes precedence and only one file for a + * given level in the inheritance tree will be used, so A$B_locale will + * be used and A_B_locale ignored. + */ + public void testNestedAnnotations() { + Nested m = GWT.create(Nested.class); + assertEquals("nested dollar b_C", m.nestedDollar()); + assertEquals("nested underscore b", m.nestedUnderscore()); + } + public void testWalkUpColorTree() { Colors colors = (Colors) GWT.create(Colors.class); assertEquals("red_b_C_d", colors.red());
diff --git a/user/test/com/google/gwt/i18n/client/I18NTest.java b/user/test/com/google/gwt/i18n/client/I18NTest.java index 1e5e2a7..5889825 100644 --- a/user/test/com/google/gwt/i18n/client/I18NTest.java +++ b/user/test/com/google/gwt/i18n/client/I18NTest.java
@@ -16,6 +16,7 @@ package com.google.gwt.i18n.client; import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.TestAnnotatedMessages.Nested; import com.google.gwt.i18n.client.gen.Colors; import com.google.gwt.i18n.client.gen.Shapes; import com.google.gwt.i18n.client.gen.TestMessages; @@ -51,6 +52,7 @@ return "com.google.gwt.i18n.I18NTest"; } + @SuppressWarnings("unchecked") // intentional test of raw map public void testAnnotatedConstants() { TestAnnotatedConstants c = GWT.create(TestAnnotatedConstants.class); assertEquals(14, c.fourteen()); @@ -115,13 +117,22 @@ assertEquals("PL: Total is US$11,305.01", m.currencyFormat(11305.01)); assertEquals("PL: Default number format is 1,017.1", m.defaultNumberFormat(1017.1)); + @SuppressWarnings("deprecation") + Date date = new Date(107, 11, 1, 12, 1, 2); assertEquals("PL: It is 12:01 PM on Saturday, December 1, 2007", - m.getTimeDate(new Date(107, 11, 1, 12, 1, 2))); + m.getTimeDate(date)); assertEquals("PL: 13 widgets", m.pluralWidgetsOther(13)); assertEquals("Too many widgets to count (150) in pig-latin", m.pluralWidgetsOther(150)); } + public void testAnnotationInheritance() { + TestAnnotationGrandchild m = GWT.create(TestAnnotationGrandchild.class); + assertEquals("foo", m.foo()); + assertEquals("bar_piglatin", m.bar()); + assertEquals("baz_piglatin", m.baz()); + } + public void testBindings() { TestBinding b = (TestBinding) GWT.create(TestBinding.class); assertEquals("default", b.a()); @@ -519,6 +530,13 @@ assertEquals("Extend Protected Inner", extendProtectedInner); } + public void testNestedAnnotations() { + Nested m = GWT.create(Nested.class); + // no translation exists in piglatin for nested dollar + assertEquals("nested dollar", m.nestedDollar()); + assertEquals("estednay underscoray", m.nestedUnderscore()); + } + public void testShapesFamily() { Shapes shapes = (Shapes) GWT.create(Shapes.class); // test overload
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_b_C.properties b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_b_C.properties new file mode 100644 index 0000000..57a9bd3 --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages$Nested_b_C.properties
@@ -0,0 +1 @@ +nestedDollar = nested dollar b_C
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages.java b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages.java index 437a559..7c0d56c 100644 --- a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages.java +++ b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages.java
@@ -26,47 +26,66 @@ * Test of Messages generation using annotations. */ @DefaultLocale("en-US") -//@GenerateKeys("com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator") -@GenerateKeys("com.google.gwt.i18n.rebind.keygen.MethodNameKeyGenerator") // default +// @GenerateKeys("com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator") +@GenerateKeys("com.google.gwt.i18n.rebind.keygen.MethodNameKeyGenerator") +// default @Generate(format = "com.google.gwt.i18n.rebind.format.PropertiesFormat") public interface TestAnnotatedMessages extends Messages { + /** + * Test of property file lookup on nested classes. + * + * nestedDollar() is redefined in a property file with a $ in it. + * nestedUnderscore() is redefined in a property file with a _ in it. + */ + public interface Nested extends Messages { + + @DefaultMessage("nested dollar") + String nestedDollar(); + + @DefaultMessage("nested underscore") + String nestedUnderscore(); + } + @DefaultMessage("Test me") String basicText(); - + @DefaultMessage("Once more, with meaning") @Meaning("Mangled quote") String withMeaning(); - + @DefaultMessage("One argument: {0}") String oneArgument(String value); - + @DefaultMessage("One argument, which is optional") - String optionalArgument(@Optional String value); - + String optionalArgument(@Optional + String value); + @DefaultMessage("Two arguments, {1} and {0}, inverted") String invertedArguments(String one, String two); - + @DefaultMessage("Don''t tell me I can''t '{'quote things in braces'}'") String quotedText(); - + @DefaultMessage("This '{0}' would be an argument if not quoted") String quotedArg(); - + @DefaultMessage("Total is {0,number,currency}") String currencyFormat(double value); - + @DefaultMessage("Default number format is {0,number}") String defaultNumberFormat(double value); - + @DefaultMessage("It is {0,time,short} on {0,date,full}") String getTimeDate(Date value); - + @DefaultMessage("{0} widgets") - @PluralText({"one", "A widget"}) - String pluralWidgetsOther(@PluralCount int count); + @PluralText( {"one", "A widget"}) + String pluralWidgetsOther(@PluralCount + int count); @DefaultMessage("{1} {0}") - @PluralText({"one", "A {0}"}) - String twoParamPlural(String name, @PluralCount int count); + @PluralText( {"one", "A {0}"}) + String twoParamPlural(String name, @PluralCount + int count); }
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_b.properties b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_b.properties new file mode 100644 index 0000000..50b477c --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_b.properties
@@ -0,0 +1 @@ +nestedUnderscore = nested underscore b
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_piglatin.properties b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_piglatin.properties new file mode 100644 index 0000000..4732950 --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/TestAnnotatedMessages_Nested_piglatin.properties
@@ -0,0 +1 @@ +nestedUnderscore = estednay underscoray
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotationGrandchild.java b/user/test/com/google/gwt/i18n/client/TestAnnotationGrandchild.java new file mode 100644 index 0000000..c69b16d --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/TestAnnotationGrandchild.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.i18n.client; + +/** + * Verifies that class-level annotations on grandparent interface are still honored, + * to make sure multiple levels of inheritance are handled. + */ +public interface TestAnnotationGrandchild extends TestAnnotationInheritance { + + @DefaultMessage("baz") + String baz(); +}
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotationGrandchild_piglatin.properties b/user/test/com/google/gwt/i18n/client/TestAnnotationGrandchild_piglatin.properties new file mode 100644 index 0000000..6dda386 --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/TestAnnotationGrandchild_piglatin.properties
@@ -0,0 +1,4 @@ +# MD5 hash of "bar" +37B51D194A7513E45B56F6524F2D51F2 = bar_piglatin +# MD5 hash of "baz" +73FEFFA4B7F6BB68E44CF984C85F6E88 = baz_piglatin
diff --git a/user/test/com/google/gwt/i18n/client/TestAnnotationInheritance.java b/user/test/com/google/gwt/i18n/client/TestAnnotationInheritance.java new file mode 100644 index 0000000..92ddcdc --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/TestAnnotationInheritance.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.i18n.client; + +/** + * Verifies that class-level annotations on superinterface are still used + * by this interface. In this case, we verify that this interface's keys + * are based on the MD5 hash of the default value. + */ +public interface TestAnnotationInheritance extends CommonInterfaceAnnotations { + + @DefaultMessage("bar") + String bar(); +}
diff --git a/user/test/com/google/gwt/i18n/client/gen/Colors.java b/user/test/com/google/gwt/i18n/client/gen/Colors.java index 2da193e..560836d 100644 --- a/user/test/com/google/gwt/i18n/client/gen/Colors.java +++ b/user/test/com/google/gwt/i18n/client/gen/Colors.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * 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 @@ -17,71 +17,79 @@ /** * Interface to represent the constants contained in resource bundle: - * com/google/gwt/i18n/client/gen/Colors.properties. + * 'com/google/gwt/i18n/client/gen/Colors.properties'. */ public interface Colors extends com.google.gwt.i18n.client.Constants { /** - * Translated "ĝrééñ". - * - * @return translated "ĝrééñ" - * @gwt.key green - */ - String green(); - - /** - * Translated "réd ". - * - * @return translated "réd " - * @gwt.key red - */ - String red(); - - /** - * Translated "ŵĥîţé". - * - * @return translated "ŵĥîţé" - * @gwt.key white - */ - String white(); - - /** - * Translated "ĝréý". - * - * @return translated "ĝréý" - * @gwt.key grey - */ - String grey(); - - /** - * Translated "bļûç". - * - * @return translated "bļûç" - * @gwt.key blue - */ - String blue(); - - /** - * Translated "ýéļļöŵ". - * - * @return translated "ýéļļöŵ" - * @gwt.key yellow - */ - String yellow(); - - /** * Translated "bļåçķ". * * @return translated "bļåçķ" - * @gwt.key black */ + @DefaultStringValue("bļåçķ") + @Key("black") String black(); /** + * Translated "bļûç". + * + * @return translated "bļûç" + */ + @DefaultStringValue("bļûç") + @Key("blue") + String blue(); + + /** + * Translated "ĝrééñ". + * + * @return translated "ĝrééñ" + */ + @DefaultStringValue("ĝrééñ") + @Key("green") + String green(); + + /** + * Translated "ĝréý". + * + * @return translated "ĝréý" + */ + @DefaultStringValue("ĝréý") + @Key("grey") + String grey(); + + /** + * Translated "réd ". + * + * @return translated "réd " + */ + @DefaultStringValue("réd ") + @Key("red") + String red(); + + /** * Translated "any primary color". * * @return translated "any primary color" - * @gwt.key shapeColor */ + @DefaultStringValue("any primary color") + @Key("shapeColor") String shapeColor(); + + /** + * Translated "ŵĥîţé". + * + * @return translated "ŵĥîţé" + */ + @DefaultStringValue("ŵĥîţé") + @Key("white") + String white(); + + /** + * Translated "ýéļļöŵ". + * + * @return translated "ýéļļöŵ" + */ + @DefaultStringValue("ýéļļöŵ") + @Key("yellow") + String yellow(); }
diff --git a/user/test/com/google/gwt/i18n/client/gen/Shapes.java b/user/test/com/google/gwt/i18n/client/gen/Shapes.java index 089cd74..1f2be04 100644 --- a/user/test/com/google/gwt/i18n/client/gen/Shapes.java +++ b/user/test/com/google/gwt/i18n/client/gen/Shapes.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * 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 @@ -17,39 +17,43 @@ /** * Interface to represent the constants contained in resource bundle: - * com/google/gwt/i18n/client/gen/Shapes.properties. + * 'com/google/gwt/i18n/client/gen/Shapes.properties'. */ public interface Shapes extends com.google.gwt.i18n.client.Constants { /** - * Translated "a triangle". - * - * @return translated "a triangle" - * @gwt.key triangle - */ - String triangle(); - - /** - * Translated "a square ". - * - * @return translated "a square " - * @gwt.key square - */ - String square(); - - /** * Translated "a circle". * * @return translated "a circle" - * @gwt.key circle */ + @DefaultStringValue("a circle") + @Key("circle") String circle(); /** * Translated "a color wheel". * * @return translated "a color wheel" - * @gwt.key shapeColor */ + @DefaultStringValue("a color wheel") + @Key("shapeColor") String shapeColor(); + + /** + * Translated "a square\u0009". + * + * @return translated "a square\u0009" + */ + @DefaultStringValue("a square\u0009") + @Key("square") + String square(); + + /** + * Translated "a triangle". + * + * @return translated "a triangle" + */ + @DefaultStringValue("a triangle") + @Key("triangle") + String triangle(); }
diff --git a/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java b/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java index 57648d9..a3cfec7 100644 --- a/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java +++ b/user/test/com/google/gwt/i18n/client/gen/SingleConstant.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * 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 @@ -17,7 +17,7 @@ /** * Interface to represent the constants contained in resource bundle: - * com/google/gwt/i18n/client/gen/SingleConstant.properties. + * 'com/google/gwt/i18n/client/gen/SingleConstant.properties'. */ public interface SingleConstant extends com.google.gwt.i18n.client.Constants { @@ -25,7 +25,8 @@ * Translated "me". * * @return translated "me" - * @gwt.key justOne */ + @DefaultStringValue("me") + @Key("justOne") String justOne(); }
diff --git a/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java b/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java index a97c277..d14e857 100644 --- a/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java +++ b/user/test/com/google/gwt/i18n/client/gen/SingleMessages.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * 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 @@ -17,7 +17,7 @@ /** * Interface to represent the messages contained in resource bundle: - * com/google/gwt/i18n/client/gen/SingleMessages.properties. + * 'com/google/gwt/i18n/client/gen/SingleMessages.properties'. */ public interface SingleMessages extends com.google.gwt.i18n.client.Messages { @@ -25,7 +25,8 @@ * Translated "me". * * @return translated "me" - * @gwt.key justOne */ + @DefaultMessage("me") + @Key("justOne") String justOne(); }
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java index 08ca867..d10bb2e 100644 --- a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java +++ b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * 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 @@ -17,17 +17,27 @@ /** * Interface to represent the constants contained in resource bundle: - * com/google/gwt/i18n/client/gen/TestBadKeys.properties. + * 'com/google/gwt/i18n/client/gen/TestBadKeys.properties'. */ public interface TestBadKeys extends com.google.gwt.i18n.client.ConstantsWithLookup { /** + * Translated "andStar". + * + * @return translated "andStar" + */ + @DefaultStringValue("andStar") + @Key("&*") + String __(); + + /** * Translated "_". * * @return translated "_" - * @gwt.key - */ + @DefaultStringValue("_") + @Key("-") String _(); /** @@ -36,159 +46,188 @@ * * @return translated * "________________________________________________________________" - * @gwt.key ---------------------------------------------------------------- */ + @DefaultStringValue("________________________________________________________________") + @Key("----------------------------------------------------------------") String ________________________________________________________________(); /** - * Translated "__dup". - * - * @return translated "__dup" - * @gwt.key . - */ - String __dup(); - - /** * Translated "__s". * * @return translated "__s" - * @gwt.key --s */ + @DefaultStringValue("__s") + @Key("--s") String __s(); /** * Translated "__s_dup". * * @return translated "__s_dup" - * @gwt.key -.s */ + @DefaultStringValue("__s_dup") + @Key("-.s") String __s_dup(); /** - * Translated "__s_dup_dup". - * - * @return translated "__s_dup_dup" - * @gwt.key ..s - */ - String __s_dup_dup(); - - /** - * Translated "_1_2_3_4". - * - * @return translated "_1_2_3_4" - * @gwt.key _1.2.3.4 - */ - String _1_2_3_4(); - - /** * Translated "_c_____". * * @return translated "_c_____" - * @gwt.key -c..-.- */ + @DefaultStringValue("_c_____") + @Key("-c..-.-") String _c_____(); /** + * Translated "__dup". + * + * @return translated "__dup" + */ + @DefaultStringValue("__dup") + @Key(".") + String __dup(); + + /** + * Translated "__s_dup_dup". + * + * @return translated "__s_dup_dup" + */ + @DefaultStringValue("__s_dup_dup") + @Key("..s") + String __s_dup_dup(); + + /** * Translated "_level". * * @return translated "_level" - * @gwt.key .level */ + @DefaultStringValue("_level") + @Key(".level") String _level(); /** - * Translated "a__b". - * - * @return translated "a__b" - * @gwt.key a-.b - */ - String a__b(); - - /** - * Translated "a_b_c". - * - * @return translated "a_b_c" - * @gwt.key a-b-c - */ - String a_b_c(); - - /** * Translated "AWT_end". * * @return translated "AWT_end" - * @gwt.key AWT.end */ + @DefaultStringValue("AWT_end") + @Key("AWT.end") String AWT_end(); /** * Translated "AWT_f5". * * @return translated "AWT_f5" - * @gwt.key AWT.f5 */ + @DefaultStringValue("AWT_f5") + @Key("AWT.f5") String AWT_f5(); /** - * Translated "cell_2_5". - * - * @return translated "cell_2_5" - * @gwt.key cell.2.5 - */ - String cell_2_5(); - - /** * Translated "Cursor_MoveDrop_32x32_File". * * @return translated "Cursor_MoveDrop_32x32_File" - * @gwt.key Cursor.MoveDrop.32x32.File */ + @DefaultStringValue("Cursor_MoveDrop_32x32_File") + @Key("Cursor.MoveDrop.32x32.File") String Cursor_MoveDrop_32x32_File(); /** + * Translated "_1_2_3_4". + * + * @return translated "_1_2_3_4" + */ + @DefaultStringValue("_1_2_3_4") + @Key("_1.2.3.4") + String _1_2_3_4(); + + /** + * Translated "a__b". + * + * @return translated "a__b" + */ + @DefaultStringValue("a__b") + @Key("a-.b") + String a__b(); + + /** + * Translated "a_b_c". + * + * @return translated "a_b_c" + */ + @DefaultStringValue("a_b_c") + @Key("a-b-c") + String a_b_c(); + + /** + * Translated "cell_2_5". + * + * @return translated "cell_2_5" + */ + @DefaultStringValue("cell_2_5") + @Key("cell.2.5") + String cell_2_5(); + + /** * Translated "entity_160". * * @return translated "entity_160" - * @gwt.key entity.160 */ + @DefaultStringValue("entity_160") + @Key("entity.160") String entity_160(); /** * Translated "logger_org_hibernate_jdbc". * * @return translated "logger_org_hibernate_jdbc" - * @gwt.key logger.org.hibernate.jdbc */ + @DefaultStringValue("logger_org_hibernate_jdbc") + @Key("logger.org.hibernate.jdbc") String logger_org_hibernate_jdbc(); /** * Translated "maven_checkstyle_properties". * * @return translated "maven_checkstyle_properties" - * @gwt.key maven.checkstyle.properties */ + @DefaultStringValue("maven_checkstyle_properties") + @Key("maven.checkstyle.properties") String maven_checkstyle_properties(); /** * Translated "maven_jdiff_old_tag". * * @return translated "maven_jdiff_old_tag" - * @gwt.key maven.jdiff.old.tag */ + @DefaultStringValue("maven_jdiff_old_tag") + @Key("maven.jdiff.old.tag") String maven_jdiff_old_tag(); /** * Translated "permissions_755". * * @return translated "permissions_755" - * @gwt.key permissions.755 */ + @DefaultStringValue("permissions_755") + @Key("permissions.755") String permissions_755(); /** * Translated "zh_spacer". * * @return translated "zh_spacer" - * @gwt.key zh.spacer */ + @DefaultStringValue("zh_spacer") + @Key("zh.spacer") String zh_spacer(); + + /** + * Translated "e". + * + * @return translated "e" + */ + @DefaultStringValue("e") + @Key("�") + String __dup_dup(); }
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties index 46ab4d5..4761eff 100644 --- a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties +++ b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys.properties
@@ -19,4 +19,5 @@ -c..-.- = _c_____ ---------------------------------------------------------------- = ________________________________________________________________ _1.2.3.4 = _1_2_3_4 - +&* = andStar +é = e
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestBadKeys_b_C_d.properties b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys_b_C_d.properties new file mode 100644 index 0000000..a036225 --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/gen/TestBadKeys_b_C_d.properties
@@ -0,0 +1 @@ +é = e in b_C_d
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.java b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.java new file mode 100644 index 0000000..a2a50af --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.java
@@ -0,0 +1,60 @@ +/* + * 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.i18n.client.gen; + +/** + * Interface to represent the constants contained in resource bundle: + * 'com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties'. + */ +public interface TestConstantsQuoting extends + com.google.gwt.i18n.client.Constants { + + /** + * Translated "Doesn''t work this way here". + * + * @return translated "Doesn''t work this way here" + */ + @DefaultStringValue("Doesn''t work this way here") + @Key("doubledQuote") + String doubledQuote(); + + /** + * Translated "Embedded\r\ncr-nl.". + * + * @return translated "Embedded\r\ncr-nl." + */ + @DefaultStringValue("Embedded\r\ncr-nl.") + @Key("embeddedCRNL") + String embeddedCRNL(); + + /** + * Translated "This line has an\nembedded newline". + * + * @return translated "This line has an\nembedded newline" + */ + @DefaultStringValue("This line has an\nembedded newline") + @Key("embeddedNL") + String embeddedNL(); + + /** + * Translated "\"Don't worry, be happy\" he said.". + * + * @return translated "\"Don't worry, be happy\" he said." + */ + @DefaultStringValue("\"Don't worry, be happy\" he said.") + @Key("embeddedQuote") + String embeddedQuote(); +}
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties new file mode 100644 index 0000000..97baf73 --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/gen/TestConstantsQuoting.properties
@@ -0,0 +1,4 @@ +embeddedNL=This line has an\nembedded newline +embeddedCRNL=Embedded\r\ncr-nl. +embeddedQuote="Don't worry, be happy" he said. +doubledQuote=Doesn''t work this way here
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestMessages.java b/user/test/com/google/gwt/i18n/client/gen/TestMessages.java index 136c8cd..022ebeb 100644 --- a/user/test/com/google/gwt/i18n/client/gen/TestMessages.java +++ b/user/test/com/google/gwt/i18n/client/gen/TestMessages.java
@@ -1,5 +1,5 @@ /* - * Copyright 2007 Google Inc. + * 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 @@ -16,107 +16,120 @@ package com.google.gwt.i18n.client.gen; /** - * Interface to represent the messages contained in resource bundle - * com/google/gwt/i18n/client/gen/TestMessages.properties. + * Interface to represent the messages contained in resource bundle: + * 'com/google/gwt/i18n/client/gen/TestMessages.properties'. */ public interface TestMessages extends com.google.gwt.i18n.client.Messages { /** - * Translated "{0},{1}, "a","b", "{0}", "{1}", ''a'', 'b', '{0}', ''{1}''". - * - * @return translated "{0},{1}, "a","b", "{0}", "{1}", ''a'', 'b', '{0}', - * ''{1}''" - * @gwt.key argsWithQuotes - */ - String argsWithQuotes(String arg0, String arg1); - - /** - * Translated "{1} is the second arg, {0} is the first". - * - * @return translated "{1} is the second arg, {0} is the first" - * @gwt.key args2 - */ - String args2(String arg0, String arg1); - - /** * Translated "no args". * * @return translated "no args" - * @gwt.key args0 */ + @DefaultMessage("no args") + @Key("args0") String args0(); /** - * Translated "{0}". - * - * @return translated "{0}" - * @gwt.key simpleMessageTest - */ - String simpleMessageTest(String arg0); - - /** - * Translated ""~" ~~ "~~~~ """. - * - * @return translated ""~" ~~ "~~~~ """ - * @gwt.key testWithXs - */ - String testWithXs(); - - /** - * Translated "arg0arg1 arg0,arg1 {0}arg4". - * - * @return translated "arg0arg1 arg0,arg1 {0}arg4" - * @gwt.key argsTest - */ - String argsTest(String arg0); - - /** - * Translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}". - * - * @return translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}" - * @gwt.key testLotsOfUsageOfArgs - */ - String testLotsOfUsageOfArgs(String arg0, String arg1); - - /** - * Translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}". - * - * @return translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}" - * @gwt.key args10 - */ - String args10(String arg0, String arg1, String arg2, String arg3, - String arg4, String arg5, String arg6, String arg7, String arg8, - String arg9); - - /** - * Translated "お{0}你{1}好". - * - * @return translated "お{0}你{1}好" - * @gwt.key unicode - */ - String unicode(String arg0, String arg1); - - /** * Translated "{0} is a arg". * * @return translated "{0} is a arg" - * @gwt.key args1 */ + @DefaultMessage("{0} is a arg") + @Key("args1") String args1(String arg0); /** - * Translated "{quoted}". + * Translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}". * - * @return translated "{quoted}" - * @gwt.key quotedBraces + * @return translated "{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}" */ - String quotedBraces(); + @DefaultMessage("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}") + @Key("args10") + String args10(String arg0, String arg1, String arg2, String arg3, + String arg4, String arg5, String arg6, String arg7, String arg8, + String arg9); + + /** + * Translated "{1} is the second arg, {0} is the first". + * + * @return translated "{1} is the second arg, {0} is the first" + */ + @DefaultMessage("{1} is the second arg, {0} is the first") + @Key("args2") + String args2(String arg0, String arg1); + + /** + * Translated "arg0arg1 arg0,arg1 {0}arg4". + * + * @return translated "arg0arg1 arg0,arg1 {0}arg4" + */ + @DefaultMessage("arg0arg1 arg0,arg1 {0}arg4") + @Key("argsTest") + String argsTest(String arg0); + + /** + * Translated "{0},{1}, \"a\",\"b\", \"{0}\", \"{1}\", ''a'', 'b', '{0}', + * ''{1}''". + * + * @return translated "{0},{1}, \"a\",\"b\", \"{0}\", \"{1}\", ''a'', 'b', + * '{0}', ''{1}''" + */ + @DefaultMessage("{0},{1}, \"a\",\"b\", \"{0}\", \"{1}\", ''a'', 'b', '{0}', ''{1}''") + @Key("argsWithQuotes") + String argsWithQuotes(String arg0, String arg1); /** * Translated "". * * @return translated "" - * @gwt.key empty */ + @DefaultMessage("") + @Key("empty") String empty(); + + /** + * Translated "'{'quoted'}'". + * + * @return translated "'{'quoted'}'" + */ + @DefaultMessage("'{'quoted'}'") + @Key("quotedBraces") + String quotedBraces(); + + /** + * Translated "{0}". + * + * @return translated "{0}" + */ + @DefaultMessage("{0}") + @Key("simpleMessageTest") + String simpleMessageTest(String arg0); + + /** + * Translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}". + * + * @return translated "repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}" + */ + @DefaultMessage("repeatedArgs: {0}, {1}, {0}, {1}, {0}, {1}, {0}, {1}") + @Key("testLotsOfUsageOfArgs") + String testLotsOfUsageOfArgs(String arg0, String arg1); + + /** + * Translated "\"~\" ~~ \"~~~~ \"\"". + * + * @return translated "\"~\" ~~ \"~~~~ \"\"" + */ + @DefaultMessage("\"~\" ~~ \"~~~~ \"\"") + @Key("testWithXs") + String testWithXs(); + + /** + * Translated "お{0}你{1}好". + * + * @return translated "お{0}你{1}好" + */ + @DefaultMessage("お{0}你{1}好") + @Key("unicode") + String unicode(String arg0, String arg1); }
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.java b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.java new file mode 100644 index 0000000..b20efdc --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.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.i18n.client.gen; + +/** + * Interface to represent the messages contained in resource bundle: + * 'com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties'. + */ +public interface TestMessagesQuoting extends + com.google.gwt.i18n.client.Messages { + + /** + * Translated "Embedded\r\ncr-nl.". + * + * @return translated "Embedded\r\ncr-nl." + */ + @DefaultMessage("Embedded\r\ncr-nl.") + @Key("embeddedCRNL") + String embeddedCRNL(); + + /** + * Translated "This line has an\nembedded newline". + * + * @return translated "This line has an\nembedded newline" + */ + @DefaultMessage("This line has an\nembedded newline") + @Key("embeddedNL") + String embeddedNL(); + + /** + * Translated "\"Don''t worry, be happy\" he said.". + * + * @return translated "\"Don''t worry, be happy\" he said." + */ + @DefaultMessage("\"Don''t worry, be happy\" he said.") + @Key("embeddedQuote") + String embeddedQuote(); +}
diff --git a/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties new file mode 100644 index 0000000..35263b8 --- /dev/null +++ b/user/test/com/google/gwt/i18n/client/gen/TestMessagesQuoting.properties
@@ -0,0 +1,3 @@ +embeddedNL=This line has an\nembedded newline +embeddedCRNL=Embedded\r\ncr-nl. +embeddedQuote="Don''t worry, be happy" he said.
diff --git a/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java b/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java index 30d0024..389359d 100644 --- a/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java +++ b/user/test/com/google/gwt/i18n/tools/I18NSyncTest_.java
@@ -50,7 +50,12 @@ // Should be caught } } - + + public void testConstantsQuoting() throws IOException { + String className = CLIENT_SOURCE_PACKAGE + "TestConstantsQuoting"; + I18NSync.createConstantsInterfaceFromClassName(className, CLIENT_SOURCE_DIR); + } + public void testFileIsDirCase() { try { I18NSync.createMessagesInterfaceFromClassName(CLIENT_SOURCE_PACKAGE, null); @@ -77,15 +82,14 @@ I18NSync.createMessagesInterfaceFromClassName(className, CLIENT_SOURCE_DIR); } + public void testMessagesQuoting() throws IOException { + String className = CLIENT_SOURCE_PACKAGE + "TestMessagesQuoting"; + I18NSync.createMessagesInterfaceFromClassName(className, CLIENT_SOURCE_DIR); + } + public void testMethodRenaming() throws IOException { String className = CLIENT_SOURCE_PACKAGE + "TestBadKeys"; I18NSync.createConstantsWithLookupInterfaceFromClassName(className, CLIENT_SOURCE_DIR); } - - public void testWarning() throws IOException { - String className = CLIENT_SOURCE_PACKAGE + "TestReallyBadKeys"; - I18NSync.createConstantsWithLookupInterfaceFromClassName(className); - } - }
diff --git a/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java b/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java index 78eac3f..db7c871 100644 --- a/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java +++ b/user/test/com/google/gwt/user/client/rpc/CollectionsTest.java
@@ -22,6 +22,8 @@ import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeHashMap; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeHashSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashMap; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeMap; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeVector; import java.sql.Time; @@ -32,6 +34,8 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.Vector; /** @@ -591,6 +595,48 @@ }); } + public void testTreeMap() { + delayTestFinish(TEST_DELAY); + + CollectionsTestServiceAsync service = getServiceAsync(); + for (boolean option : new boolean[] {true, false}) { + final TreeMap<String, MarkerTypeTreeMap> expected = TestSetFactory.createTreeMap(option); + service.echo(expected, option, + new AsyncCallback<TreeMap<String, MarkerTypeTreeMap>>() { + public void onFailure(Throwable caught) { + TestSetValidator.rethrowException(caught); + } + + public void onSuccess(TreeMap<String, MarkerTypeTreeMap> result) { + assertNotNull(result); + assertTrue(TestSetValidator.isValid(expected, result)); + finishTest(); + } + }); + } + } + + public void testTreeSet() { + delayTestFinish(TEST_DELAY); + + CollectionsTestServiceAsync service = getServiceAsync(); + for (boolean option : new boolean[] {true, false}) { + final TreeSet<MarkerTypeTreeSet> expected = TestSetFactory.createTreeSet(option); + service.echo(expected, option, + new AsyncCallback<TreeSet<MarkerTypeTreeSet>>() { + public void onFailure(Throwable caught) { + TestSetValidator.rethrowException(caught); + } + + public void onSuccess(TreeSet<MarkerTypeTreeSet> result) { + assertNotNull(result); + assertTrue(TestSetValidator.isValid(expected, result)); + finishTest(); + } + }); + } + } + public void testVector() { delayTestFinish(TEST_DELAY);
diff --git a/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java b/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java index 38590f9..6560d31 100644 --- a/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java +++ b/user/test/com/google/gwt/user/client/rpc/CollectionsTestService.java
@@ -21,6 +21,8 @@ import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeHashSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashMap; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashSet; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeMap; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeVector; import java.sql.Time; @@ -32,6 +34,8 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.Vector; /** @@ -50,6 +54,7 @@ super(msg); } } + ArrayList<MarkerTypeArrayList> echo(ArrayList<MarkerTypeArrayList> value) throws CollectionsTestServiceException; @@ -113,6 +118,13 @@ Timestamp[] echo(Timestamp[] value) throws CollectionsTestServiceException; + TreeMap<String, MarkerTypeTreeMap> echo( + TreeMap<String, MarkerTypeTreeMap> value, boolean option) + throws CollectionsTestServiceException; + + TreeSet<MarkerTypeTreeSet> echo(TreeSet<MarkerTypeTreeSet> value, + boolean option) throws CollectionsTestServiceException; + Vector<MarkerTypeVector> echo(Vector<MarkerTypeVector> value) throws CollectionsTestServiceException;
diff --git a/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java index 5423060..66fe565 100644 --- a/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java +++ b/user/test/com/google/gwt/user/client/rpc/CollectionsTestServiceAsync.java
@@ -21,6 +21,8 @@ import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeHashSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashMap; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashSet; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeMap; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeVector; import java.sql.Time; @@ -32,6 +34,8 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.Vector; /** @@ -94,7 +98,13 @@ void echo(String[][] value, AsyncCallback<String[][]> callback); void echo(Time[] value, AsyncCallback<Time[]> callback); + + void echo(TreeMap<String, MarkerTypeTreeMap> value, boolean option, + AsyncCallback<TreeMap<String, MarkerTypeTreeMap>> callback); + void echo(TreeSet<MarkerTypeTreeSet> value, boolean option, + AsyncCallback<TreeSet<MarkerTypeTreeSet>> callback); + void echo(Timestamp[] value, AsyncCallback<Timestamp[]> callback); void echo(Vector<MarkerTypeVector> value,
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java index 8bb6dd9..a9fdeff 100644 --- a/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java +++ b/user/test/com/google/gwt/user/client/rpc/TestSetFactory.java
@@ -15,16 +15,20 @@ */ package com.google.gwt.user.client.rpc; +import java.io.Serializable; import java.sql.Time; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.Vector; /** @@ -37,7 +41,7 @@ * exposure in various collections. */ public static class MarkerBase implements IsSerializable { - public String value; + private String value; public MarkerBase(String value) { this.value = value; @@ -53,6 +57,10 @@ return false; } + public String getValue() { + return value; + } + @Override public int hashCode() { return value.hashCode(); @@ -157,6 +165,43 @@ * A single-use marker type to independently check type parameter exposure in * various collections. */ + public static final class MarkerTypeTreeMap extends MarkerBase { + + public MarkerTypeTreeMap(String value) { + super(value); + } + + MarkerTypeTreeMap() { + super(null); + } + } + + /** + * A single-use marker type to independently check type parameter exposure in + * various collections. + */ + public static final class MarkerTypeTreeSet extends MarkerBase implements + Comparable<MarkerTypeTreeSet> { + + public MarkerTypeTreeSet(String value) { + super(value); + } + + MarkerTypeTreeSet() { + super(null); + } + + // if getValue() returns null, a null-pointer expection will be thrown, + // which is the intended effect + public int compareTo(MarkerTypeTreeSet arg0) { + return getValue().compareTo(arg0.getValue()); + } + } + + /** + * A single-use marker type to independently check type parameter exposure in + * various collections. + */ public static final class MarkerTypeVector extends MarkerBase { public MarkerTypeVector(String value) { @@ -236,6 +281,35 @@ public static class UnserializableNode { } + static class ReverseSorter<T extends Comparable<T>> implements Comparator<T>, + Serializable { + + // for gwt-serialization + ReverseSorter() { + } + + public int compare(T a, T b) { + // Explicit null check to match JRE specs + if (a == null || b == null) { + throw new NullPointerException(); + } + return b.compareTo(a); + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object ob) { + if (!(ob instanceof ReverseSorter)) { + return false; + } + return true; + } + } + public static ArrayList<MarkerTypeArrayList> createArrayList() { ArrayList<MarkerTypeArrayList> list = new ArrayList<MarkerTypeArrayList>(); list.add(new MarkerTypeArrayList("foo")); @@ -425,6 +499,38 @@ "valueOf", "constructor", "__proto__"}; } + public static TreeMap<String, MarkerTypeTreeMap> createTreeMap( + boolean defaultComparator) { + TreeMap<String, MarkerTypeTreeMap> map; + if (defaultComparator) { + map = new TreeMap<String, MarkerTypeTreeMap>(); + } else { + map = new TreeMap<String, MarkerTypeTreeMap>(new ReverseSorter<String>()); + } + map.put("foo", new MarkerTypeTreeMap("foo")); + map.put("bar", new MarkerTypeTreeMap("bar")); + map.put("baz", new MarkerTypeTreeMap("baz")); + map.put("bal", new MarkerTypeTreeMap("bal")); + map.put("w00t", new MarkerTypeTreeMap("w00t")); + return map; + } + + public static TreeSet<MarkerTypeTreeSet> createTreeSet( + boolean defaultComparator) { + TreeSet<MarkerTypeTreeSet> set; + if (defaultComparator) { + set = new TreeSet<MarkerTypeTreeSet>(); + } else { + set = new TreeSet<MarkerTypeTreeSet>(new ReverseSorter<MarkerTypeTreeSet>()); + } + set.add(new MarkerTypeTreeSet("foo")); + set.add(new MarkerTypeTreeSet("bar")); + set.add(new MarkerTypeTreeSet("baz")); + set.add(new MarkerTypeTreeSet("bal")); + set.add(new MarkerTypeTreeSet("w00t")); + return set; + } + public static Vector<MarkerTypeVector> createVector() { Vector<MarkerTypeVector> vector = new Vector<MarkerTypeVector>(); vector.add(new MarkerTypeVector("foo"));
diff --git a/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java b/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java index da95a6d..a0b21f8 100644 --- a/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java +++ b/user/test/com/google/gwt/user/client/rpc/TestSetValidator.java
@@ -15,6 +15,8 @@ */ package com.google.gwt.user.client.rpc; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeMap; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeSet; import com.google.gwt.user.client.rpc.TestSetFactory.SerializableDoublyLinkedNode; import com.google.gwt.user.client.rpc.TestSetFactory.SerializablePrivateNoArg; import com.google.gwt.user.client.rpc.TestSetFactory.SerializableWithTwoArrays; @@ -26,7 +28,10 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.Vector; import java.util.Map.Entry; @@ -318,6 +323,60 @@ return node.one == node.two; } + // also checks whether the sorting of entries is maintained or not. + public static boolean isValid(TreeMap<String, MarkerTypeTreeMap> expected, + TreeMap<String, MarkerTypeTreeMap> map) { + if (map == null) { + return false; + } + if (!equalsWithNullCheck(map.comparator(), expected.comparator())) { + return false; + } + int size = 0; + if ((size = expected.size()) != map.size()) { + return false; + } + // entrySet returns entries in the sorted order + List<Map.Entry<String, MarkerTypeTreeMap>> actualList = new ArrayList<Map.Entry<String, MarkerTypeTreeMap>>( + map.entrySet()); + List<Map.Entry<String, MarkerTypeTreeMap>> expectedList = new ArrayList<Map.Entry<String, MarkerTypeTreeMap>>( + expected.entrySet()); + for (int index = 0; index < size; index++) { + Entry<String, MarkerTypeTreeMap> expectedEntry = expectedList.get(index); + Entry<String, MarkerTypeTreeMap> actualEntry = actualList.get(index); + if (!equalsWithNullCheck(expectedEntry.getKey(), actualEntry.getKey()) + || !equalsWithNullCheck(expectedEntry.getValue(), + actualEntry.getValue())) { + return false; + } + } + return true; + } + + // also checks whether the sorting of entries is maintained or not. + public static boolean isValid(TreeSet<MarkerTypeTreeSet> expected, + TreeSet<MarkerTypeTreeSet> set) { + if (set == null) { + return false; + } + if (!equalsWithNullCheck(set.comparator(), expected.comparator())) { + return false; + } + int size = 0; + if ((size = expected.size()) != set.size()) { + return false; + } + // entrySet returns entries in the sorted order + List<MarkerTypeTreeSet> actualList = new ArrayList<MarkerTypeTreeSet>(set); + List<MarkerTypeTreeSet> expectedList = new ArrayList<MarkerTypeTreeSet>(expected); + for (int index = 0; index < size; index++) { + if (!equalsWithNullCheck(expectedList.get(index), actualList.get(index))) { + return false; + } + } + return true; + } + public static boolean isValid(Vector expected, Vector actual) { if (actual == null) { return false; @@ -446,6 +505,12 @@ return true; } + /** + * Wrap an exception in RuntimeException if necessary so it doesn't have to be listed in + * throws clauses. + * + * @param caught exception to wrap + */ public static void rethrowException(Throwable caught) { if (caught instanceof RuntimeException) { throw (RuntimeException) caught; @@ -453,5 +518,8 @@ throw new RuntimeException(caught); } } + private static boolean equalsWithNullCheck(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java index 7edbbfa..d31416f 100644 --- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java +++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingService.java
@@ -19,12 +19,69 @@ * Service used to test unicode escaping. */ public interface UnicodeEscapingService extends RemoteService { + + /** + * Exception for escaping errors. + */ + public static class InvalidCharacterException extends Exception { + + private static String toHex(int val) { + String hex = Integer.toHexString(val); + return "00000".substring(hex.length()) + hex; + } + + private int index; + private int expected; + private int actual; + + protected InvalidCharacterException() { } + + public InvalidCharacterException(int index, int expected, int actual) { + super(index < 0 ? "String length mismatch: expected = " + expected + ", actual = " + actual + : "At index " + index + ", expected = U+" + toHex(expected) + ", actual = U+" + + toHex(actual)); + this.index = index; + this.expected = expected; + this.actual = actual; + } + + public int getActual() { + return actual; + } + + public int getExpected() { + return expected; + } + + public int getIndex() { + return index; + } + } + /** * Returns a string containing the characters from start to end. * - * @param start start character value, inclusive - * @param end end character value, exclusive + * Used to verify server->client escaping. + * + * @param start start character value, inclusive -- note if greater + * than {@link Character#MIN_SUPPLEMENTARY_CODE_POINT} it will + * be included as surrogate pairs in the returned string. + * @param end end character value, exclusive (see above comment) * @return a string containing the characters from start to end */ String getStringContainingCharacterRange(int start, int end); + + /** + * Verifies that the string contains the specified characters. + * + * Used to verify client->server escaping. + * + * @param start start code point value included + * @param end first code point not included + * @param str string to verify + * @throws InvalidCharacterException if the string does not contain the specified characters + * @return true if the verification succeeded + */ + boolean verifyStringContainingCharacterRange(int start, int end, String str) + throws InvalidCharacterException; }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java index f563218..6ae9fd4 100644 --- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java +++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingServiceAsync.java
@@ -15,10 +15,14 @@ */ package com.google.gwt.user.client.rpc; +import com.google.gwt.user.client.rpc.UnicodeEscapingService.InvalidCharacterException; + /** * Async version of the {@link UnicodeEscapingService} interface. */ public interface UnicodeEscapingServiceAsync { void getStringContainingCharacterRange(int start, int end, - AsyncCallback callback); + AsyncCallback<String> callback); + void verifyStringContainingCharacterRange(int start, int end, String str, + AsyncCallback<Boolean> callback) throws InvalidCharacterException; }
diff --git a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java index 1c5f989..f45777e 100644 --- a/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java +++ b/user/test/com/google/gwt/user/client/rpc/UnicodeEscapingTest.java
@@ -17,68 +17,256 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.junit.client.GWTTestCase; +import com.google.gwt.user.client.rpc.UnicodeEscapingService.InvalidCharacterException; /** - * Test which verifies that we properly escape JSON strings sent back from the - * server. + * Test that any valid string can be sent via RPC in both directions. + * + * TODO(jat): make unpaired surrogates work properly if it is possible to do + * so on all browsers, then add them to this test. */ public class UnicodeEscapingTest extends GWTTestCase { - private static final int DEFAULT_TEST_FINISH_DELAY_MS = 5000; - private static final int CHARACTER_RANGE_SIZE = 1024; - private static final int LAST_CHARACTER = 0x10000; + /** the size of a block of characters to test */ + private static final int CHARACTER_BLOCK_SIZE = 64; - private int start = 0; + /** + * When doing the non-BMP test, we don't test every block of characters + * because it takes too long - this is the increment to use. It is not a + * power of two so we alter the alignment of the block of characters we skip. + */ + private static final int NON_BMP_TEST_INCREMENT = 8192 + 64; + /** the time to wait for the test of a block of characters */ + private static final int TEST_FINISH_DELAY_MS = 500000; + + /** + * Generates a string containing a sequence of code points. + * + * @param start first code point to include in the string + * @param end one past the last code point to include in the string + * @return a string containing all the requested code points + */ + public static String getStringContainingCharacterRange(int start, int end) { + StringBuffer buf = new StringBuffer(); + for (int codePoint = start; codePoint < end; ++codePoint) { + if (Character.isSupplementaryCodePoint(codePoint)) { + buf.append(Character.toChars(codePoint)); + } else { + buf.append((char) codePoint); + } + } + + return buf.toString(); + } + + /* + * Copied from HistoryTest. + */ + private static native boolean isSafari2() /*-{ + var exp = / AppleWebKit\/([\d]+)/; + var result = exp.exec(navigator.userAgent); + if (result) { + // The standard history implementation works fine on WebKit >= 522 + // (Safari 3 beta). + if (parseInt(result[1]) >= 522) { + return false; + } + } + + // The standard history implementation works just fine on the iPhone, which + // unfortunately reports itself as WebKit/420+. + if (navigator.userAgent.indexOf('iPhone') != -1) { + return false; + } + + return true; + }-*/; + /** + * Verifies that the supplied string includes the requested code points. + * + * @param start first code point to include in the string + * @param end one past the last code point to include in the string + * @param str the string to test + * @throws InvalidCharacterException if a character doesn't match + * @throws RuntimeException if the string is too long + */ + public static void verifyStringContainingCharacterRange(int start, int end, + String str) throws InvalidCharacterException { + if (str == null) { + throw new NullPointerException("String is null"); + } + int expectedLen = end - start; + int strLen = str.codePointCount(0, str.length()); + for (int i = 0, codePoint = start; i < strLen; + i = Character.offsetByCodePoints(str, i, 1)) { + int strCodePoint = str.codePointAt(i); + if (strCodePoint != codePoint) { + throw new InvalidCharacterException(i, codePoint, strCodePoint); + } + ++codePoint; + } + if (strLen < expectedLen) { + throw new InvalidCharacterException(strLen, start + strLen, -1); + } else if (expectedLen != strLen) { + throw new RuntimeException("Too many characters returned on block from U+" + + Integer.toHexString(start) + " to U+" + Integer.toHexString(end) + + ": expected=" + expectedLen + ", actual=" + strLen); + } + } private static UnicodeEscapingServiceAsync getService() { - UnicodeEscapingServiceAsync service = (UnicodeEscapingServiceAsync) GWT.create(UnicodeEscapingService.class); + UnicodeEscapingServiceAsync service = GWT.create( + UnicodeEscapingService.class); ServiceDefTarget target = (ServiceDefTarget) service; target.setServiceEntryPoint(GWT.getModuleBaseURL() + "unicodeEscape"); return service; } + /** start of current block being tested */ + private int current; + + @Override public String getModuleName() { return "com.google.gwt.user.RPCSuite"; } /** + * Generate strings containing ranges of characters and sends them to the + * server for verification. This ensures that client->server string escaping + * properly handles all BMP characters. + * + * Unpaired or improperly paired surrogates are not tested here, as some + * browsers refuse to accept them. Properly paired surrogates are tested + * in the non-BMP test. + * + * Note that this does not test all possible combinations, which might be an + * issue, particularly with combining marks, though they should be logically + * equivalent in that case. + * + * @throws InvalidCharacterException + */ + public void testClientToServerBMP() throws InvalidCharacterException { + delayTestFinish(TEST_FINISH_DELAY_MS); + if (isSafari2()) { + // Safari2 can't be fixed for many characters, including null + // We only guarantee that basic ISO-Latin characters are unmolested. + clientToServerVerifyRange(0x0001, 0x0300, CHARACTER_BLOCK_SIZE, + CHARACTER_BLOCK_SIZE); + } else { + clientToServerVerifyRange(Character.MIN_CODE_POINT, + Character.MIN_SURROGATE, CHARACTER_BLOCK_SIZE, + CHARACTER_BLOCK_SIZE); + clientToServerVerifyRange(Character.MAX_SURROGATE + 1, + Character.MIN_SUPPLEMENTARY_CODE_POINT, CHARACTER_BLOCK_SIZE, + CHARACTER_BLOCK_SIZE); + } + } + + /** + * Generate strings containing ranges of characters and sends them to the + * server for verification. This ensures that client->server string escaping + * properly handles all non-BMP characters. + * + * Note that this does not test all possible combinations, which might be an + * issue, particularly with combining marks, though they should be logically + * equivalent in that case. + * + * @throws InvalidCharacterException + */ + public void testClientToServerNonBMP() throws InvalidCharacterException { + delayTestFinish(TEST_FINISH_DELAY_MS); + clientToServerVerifyRange(Character.MIN_SUPPLEMENTARY_CODE_POINT, + Character.MAX_CODE_POINT + 1, CHARACTER_BLOCK_SIZE, + NON_BMP_TEST_INCREMENT); + } + + /** + * Requests strings of CHARACTER_RANGE_SIZE from the server and validates + * that the returned string length matches CHARACTER_RANGE_SIZE and that all + * of the characters remain intact. + * + * Note that this does not test all possible combinations, which might be an + * issue, particularly with combining marks, though they should be logically + * equivalent in that case. + */ + public void testServerToClientBMP() { + delayTestFinish(TEST_FINISH_DELAY_MS); + serverToClientVerify(Character.MIN_CODE_POINT, + Character.MIN_SUPPLEMENTARY_CODE_POINT, CHARACTER_BLOCK_SIZE, + CHARACTER_BLOCK_SIZE); + } + + /** * Requests strings of CHARACTER_RANGE_SIZE from the server and validates that * the returned string length matches CHARACTER_RANGE_SIZE and that all of the - * characters remain intact. + * characters remain intact. Note that this test verifies non-BMP characters + * (ie, those which are represented as pairs of surrogates). + * + * Note that this does not test all possible combinations, which might be an + * issue, particularly with combining marks, though they should be logically + * equivalent in that case. */ - public void testUnicodeEscaping() { - delayTestFinish(DEFAULT_TEST_FINISH_DELAY_MS); + public void testServerToClientNonBMP() { + delayTestFinish(TEST_FINISH_DELAY_MS); + serverToClientVerify(Character.MIN_SUPPLEMENTARY_CODE_POINT, + Character.MAX_CODE_POINT + 1, CHARACTER_BLOCK_SIZE, + NON_BMP_TEST_INCREMENT); + } - getService().getStringContainingCharacterRange(0, CHARACTER_RANGE_SIZE, - new AsyncCallback() { - public void onFailure(Throwable caught) { - TestSetValidator.rethrowException(caught); + private void clientToServerVerifyRange(final int start, final int end, + final int size, final int step) throws InvalidCharacterException { + current = start; + int blockEnd = Math.min(end, current + size); + getService().verifyStringContainingCharacterRange(current, blockEnd, + getStringContainingCharacterRange(start, blockEnd), + new AsyncCallback<Boolean>() { + public void onFailure(Throwable caught) { + TestSetValidator.rethrowException(caught); + } + + public void onSuccess(Boolean ignored) { + current += step; + if (current < end) { + delayTestFinish(TEST_FINISH_DELAY_MS); + int blockEnd = Math.min(end, current + size); + try { + getService().verifyStringContainingCharacterRange(current, blockEnd, + getStringContainingCharacterRange(current, blockEnd), this); + } catch (InvalidCharacterException e) { + TestSetValidator.rethrowException(e); } + } else { + finishTest(); + } + } + }); + } - public void onSuccess(Object result) { - String str = (String) result; + private void serverToClientVerify(final int start, final int end, + final int size, final int step) { + current = start; + getService().getStringContainingCharacterRange(start, Math.min(end, + current + size), new AsyncCallback<String>() { + public void onFailure(Throwable caught) { + TestSetValidator.rethrowException(caught); + } - assertTrue("expected: " + Integer.toString(CHARACTER_RANGE_SIZE) - + " actual: " + str.length() + " for character range [" - + Integer.toString(start) + ", " - + Integer.toString(start + CHARACTER_RANGE_SIZE) + ")", - CHARACTER_RANGE_SIZE == str.length()); - - char[] chars = str.toCharArray(); - for (int i = 0; i < CHARACTER_RANGE_SIZE; ++i) { - assertEquals(i + start, chars[i]); - } - - start += CHARACTER_RANGE_SIZE; - if (start < LAST_CHARACTER) { - delayTestFinish(DEFAULT_TEST_FINISH_DELAY_MS); - - getService().getStringContainingCharacterRange(start, - start + CHARACTER_RANGE_SIZE, this); - } else { - finishTest(); - } - } - }); + public void onSuccess(String str) { + try { + verifyStringContainingCharacterRange(current, Math.min(end, + current + size), str); + } catch (InvalidCharacterException e) { + TestSetValidator.rethrowException(e); + } + current += step; + if (current < end) { + delayTestFinish(TEST_FINISH_DELAY_MS); + getService().getStringContainingCharacterRange(current, + Math.min(end, current + size), this); + } else { + finishTest(); + } + } + }); } }
diff --git a/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java index 12bd35b..d02c6e1 100644 --- a/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java +++ b/user/test/com/google/gwt/user/server/rpc/CollectionsTestServiceImpl.java
@@ -24,6 +24,8 @@ import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeHashSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashMap; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeLinkedHashSet; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeMap; +import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeTreeSet; import com.google.gwt.user.client.rpc.TestSetFactory.MarkerTypeVector; import java.sql.Time; @@ -36,6 +38,8 @@ import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.TreeMap; +import java.util.TreeSet; import java.util.Vector; /** @@ -328,6 +332,29 @@ return actual; } + public TreeMap<String, MarkerTypeTreeMap> echo( + TreeMap<String, MarkerTypeTreeMap> actual, boolean option) + throws CollectionsTestServiceException { + TreeMap<String, MarkerTypeTreeMap> expected = TestSetFactory.createTreeMap(option); + if (!TestSetValidator.isValid(expected, actual)) { + throw new CollectionsTestServiceException("expected: " + + expected.toString() + " actual: " + actual.toString()); + } + + return actual; + } + + public TreeSet<MarkerTypeTreeSet> echo(TreeSet<MarkerTypeTreeSet> actual, + boolean option) throws CollectionsTestServiceException { + TreeSet<MarkerTypeTreeSet> expected = TestSetFactory.createTreeSet(option); + if (!TestSetValidator.isValid(expected, actual)) { + throw new CollectionsTestServiceException("expected: " + + expected.toString() + " actual: " + actual.toString()); + } + + return actual; + } + public Vector<MarkerTypeVector> echo(Vector<MarkerTypeVector> actual) throws CollectionsTestServiceException { Vector<MarkerTypeVector> expected = TestSetFactory.createVector(); @@ -348,4 +375,5 @@ return value; } + }
diff --git a/user/test/com/google/gwt/user/server/rpc/RPCTest.java b/user/test/com/google/gwt/user/server/rpc/RPCTest.java index 8eb1bb3..20dd1b9 100644 --- a/user/test/com/google/gwt/user/server/rpc/RPCTest.java +++ b/user/test/com/google/gwt/user/server/rpc/RPCTest.java
@@ -15,10 +15,14 @@ */ package com.google.gwt.user.server.rpc; +import static com.google.gwt.user.client.rpc.impl.AbstractSerializationStream.RPC_SEPARATOR_CHAR; + import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.SerializableException; import com.google.gwt.user.client.rpc.SerializationException; +import com.google.gwt.user.client.rpc.impl.AbstractSerializationStream; +import com.google.gwt.user.server.rpc.impl.ServerSerializationStreamReader; import junit.framework.TestCase; @@ -27,6 +31,7 @@ /** * Tests for the {@link com.google.gwt.user.server.rpc.RPC RPC} class. */ +@SuppressWarnings("deprecation") public class RPCTest extends TestCase { private static interface A extends RemoteService { @@ -41,50 +46,74 @@ void method1(); } - private static final String VALID_ENCODED_REQUEST = "4\uffff" + // version - "0\uffff" + // flags - "4\uffff" + // string table entry count - A.class.getName() + "\uffff" + // string table entry #0 - "method2" + "\uffff" + // string table entry #1 - "moduleBaseURL" + "\uffff" + // string table entry #2 - "whitelistHashcode" + "\uffff" + // string table entry #4 - "3\uffff" + // module base URL - "4\uffff" + // whitelist hashcode - "1\uffff" + // interface name - "2\uffff" + // method name - "0\uffff"; // param count + private static final String VALID_ENCODED_REQUEST = "" + + AbstractSerializationStream.SERIALIZATION_STREAM_VERSION + + RPC_SEPARATOR_CHAR + // version + "0" + RPC_SEPARATOR_CHAR + // flags + "4" + RPC_SEPARATOR_CHAR + // string table entry count + A.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1 + "method2" + RPC_SEPARATOR_CHAR + // string table entry #2 + "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3 + "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4 + "3" + RPC_SEPARATOR_CHAR + // module base URL + "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode + "1" + RPC_SEPARATOR_CHAR + // interface name + "2" + RPC_SEPARATOR_CHAR + // method name + "0" + RPC_SEPARATOR_CHAR; // param count - private static final String INVALID_METHOD_REQUEST = "4\uffff" + // version - "0\uffff" + // flags - "4\uffff" + // string table entry count - A.class.getName() + "\uffff" + // string table entry #0 - "method3" + "\uffff" + // string table entry #1 - "moduleBaseURL" + "\uffff" + // string table entry #2 - "whitelistHashcode" + "\uffff" + // string table entry #4 - "3\uffff" + // module base URL - "4\uffff" + // whitelist hashcode - "1\uffff" + // interface name - "2\uffff" + // method name - "0\uffff"; // param count + private static final String INVALID_METHOD_REQUEST = "" + + AbstractSerializationStream.SERIALIZATION_STREAM_VERSION + + RPC_SEPARATOR_CHAR + // version + "0" + RPC_SEPARATOR_CHAR + // flags + "4" + RPC_SEPARATOR_CHAR + // string table entry count + A.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1 + "method3" + RPC_SEPARATOR_CHAR + // string table entry #2 + "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3 + "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4 + "3" + RPC_SEPARATOR_CHAR + // module base URL + "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode + "1" + RPC_SEPARATOR_CHAR + // interface name + "2" + RPC_SEPARATOR_CHAR + // method name + "0" + RPC_SEPARATOR_CHAR; // param count - private static final String INVALID_INTERFACE_REQUEST = "4\uffff" + // version - "0\uffff" + // flags - "4\uffff" + // string table entry count - B.class.getName() + "\uffff" + // string table entry #0 - "method1" + "\uffff" + // string table entry #1 - "moduleBaseURL" + "\uffff" + // string table entry #2 - "whitelistHashcode" + "\uffff" + // string table entry #4 - "3\uffff" + // module base URL - "4\uffff" + // whitelist hashcode - "1\uffff" + // interface name - "2\uffff" + // method name - "0\uffff"; // param count + private static final String INVALID_INTERFACE_REQUEST = "" + + AbstractSerializationStream.SERIALIZATION_STREAM_VERSION + + RPC_SEPARATOR_CHAR + // version + "0" + RPC_SEPARATOR_CHAR + // flags + "4" + RPC_SEPARATOR_CHAR + // string table entry count + B.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1 + "method1" + RPC_SEPARATOR_CHAR + // string table entry #2 + "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3 + "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4 + "3" + RPC_SEPARATOR_CHAR + // module base URL + "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode + "1" + RPC_SEPARATOR_CHAR + // interface name + "2" + RPC_SEPARATOR_CHAR + // method name + "0" + RPC_SEPARATOR_CHAR; // param count + + private static final String STRING_QUOTE_REQUEST = "" + + AbstractSerializationStream.SERIALIZATION_STREAM_VERSION + + RPC_SEPARATOR_CHAR + // version + "0" + RPC_SEPARATOR_CHAR + // flags + "7" + RPC_SEPARATOR_CHAR + // string table entry count + A.class.getName() + RPC_SEPARATOR_CHAR + // string table entry #1 + "method2" + RPC_SEPARATOR_CHAR + // string table entry #2 + "moduleBaseURL" + RPC_SEPARATOR_CHAR + // string table entry #3 + "whitelistHashcode" + RPC_SEPARATOR_CHAR + // string table entry #4 + "Raw backslash \\\\" + RPC_SEPARATOR_CHAR + // string table entry #5 + "Quoted separator \\!" + RPC_SEPARATOR_CHAR + // string table entry #6 + "\\uffff\\\\!\\\\0\\0" + RPC_SEPARATOR_CHAR + // string table entry #7 + "3" + RPC_SEPARATOR_CHAR + // module base URL + "4" + RPC_SEPARATOR_CHAR + // whitelist hashcode + "5" + RPC_SEPARATOR_CHAR + // begin test data + "6" + RPC_SEPARATOR_CHAR + + "7" + RPC_SEPARATOR_CHAR; private static final String VALID_V2_ENCODED_REQUEST = "2\uffff" + // version "0\uffff" + // flags "2\uffff" + // string table entry count - A.class.getName() + "\uffff" + // string table entry #0 - "method2" + "\uffff" + // string table entry #1 + A.class.getName() + "\uffff" + // string table entry #1 + "method2\uffff" + // string table entry #2 "1\uffff" + // interface name "2\uffff" + // method name "0\uffff"; // param count @@ -92,9 +121,22 @@ private static final String VALID_V3_ENCODED_REQUEST = "3\uffff" + // version "0\uffff" + // flags "4\uffff" + // string table entry count - A.class.getName() + "\uffff" + // string table entry #0 - "method2" + "\uffff" + // string table entry #1 - "moduleBaseURL" + "\uffff" + // string table entry #2 + A.class.getName() + "\uffff" + // string table entry #1 + "method2\uffff" + // string table entry #2 + "moduleBaseURL\uffff" + // string table entry #3 + "whitelistHashcode\uffff" + // string table entry #4 + "3\uffff" + // module base URL + "4\uffff" + // whitelist hashcode + "1\uffff" + // interface name + "2\uffff" + // method name + "0\uffff"; // param count + + private static final String VALID_V4_ENCODED_REQUEST = "4\uffff" + // version + "0\uffff" + // flags + "4\uffff" + // string table entry count + A.class.getName() + "\uffff" + // string table entry #1 + "method2" + "\uffff" + // string table entry #2 + "moduleBaseURL" + "\uffff" + // string table entry #3 "whitelistHashcode" + "\uffff" + // string table entry #4 "3\uffff" + // module base URL "4\uffff" + // whitelist hashcode @@ -120,6 +162,13 @@ } catch (IncompatibleRemoteServiceException e) { // Expected } + + try { + RPC.decodeRequest(VALID_V4_ENCODED_REQUEST, A.class, null); + fail("Should have thrown an IncompatibleRemoteServiceException"); + } catch (IncompatibleRemoteServiceException e) { + // Expected + } } /** @@ -402,4 +451,12 @@ } }, A_method1, null); } + + public void testSerializationStreamDequote() throws SerializationException { + ServerSerializationStreamReader reader = new ServerSerializationStreamReader(null, null); + reader.prepareToRead(STRING_QUOTE_REQUEST); + assertEquals("Raw backslash \\", reader.readString()); + assertEquals("Quoted separator " + RPC_SEPARATOR_CHAR, reader.readString()); + assertEquals("\uffff\\!\\0\u0000", reader.readString()); + } }
diff --git a/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java b/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java index 76bd2e7..7d4e99f 100644 --- a/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java +++ b/user/test/com/google/gwt/user/server/rpc/UnicodeEscapingServiceImpl.java
@@ -16,6 +16,7 @@ package com.google.gwt.user.server.rpc; import com.google.gwt.user.client.rpc.UnicodeEscapingService; +import com.google.gwt.user.client.rpc.UnicodeEscapingTest; /** * Implementation of the {@link UnicodeEscapingService} interface. @@ -24,18 +25,18 @@ UnicodeEscapingService { /** - * @see com.google.gwt.user.client.rpc.UnicodeEscapingService#getStringContainingCharacterRange(int, - * int) + * @see UnicodeEscapingService#getStringContainingCharacterRange(int, int) */ public String getStringContainingCharacterRange(int start, int end) { - int nChars = end - start; - - char[] chars = new char[nChars]; - for (int i = 0; i < nChars; ++i) { - char ch = (char) (start + i); - chars[i] = ch; - } + return UnicodeEscapingTest.getStringContainingCharacterRange(start, end); + } - return new String(chars); + /** + * @see UnicodeEscapingService#verifyStringContainingCharacterRange(int, int, String) + */ + public boolean verifyStringContainingCharacterRange(int start, int end, + String str) throws InvalidCharacterException { + UnicodeEscapingTest.verifyStringContainingCharacterRange(start, end, str); + return true; } }
diff --git a/user/test/com/google/gwt/xml/client/XMLTest.java b/user/test/com/google/gwt/xml/client/XMLTest.java index a34f366..180bad7 100644 --- a/user/test/com/google/gwt/xml/client/XMLTest.java +++ b/user/test/com/google/gwt/xml/client/XMLTest.java
@@ -312,7 +312,13 @@ assertEquals(top.getChildNodes().getLength(), 1); } - public void testParse() { + /** + * This test is failing on one Safari configuration in web mode in the 1.5 + * release branch, but it passes in all other configurations and in the trunk. + * The files in the xml package are identical between the trunk and the 1.5 + * branch. + */ + public void disabledTestParse() { Document docA = XMLParser.parse("<!--hello--> <a spam=\"ham\">\n <?pi hello ?>dfgdfg <b/>\t</a>"); Document docB = XMLParser.createDocument();