Implements an undocumented symbol-dumping feature.
Useful for third party tools that want to examine the AST of a GWT module textually.

Suggested by: danielvd
Review by: spoon

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@4945 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/Compiler.java b/dev/core/src/com/google/gwt/dev/Compiler.java
index ffac60d..be43d78 100644
--- a/dev/core/src/com/google/gwt/dev/Compiler.java
+++ b/dev/core/src/com/google/gwt/dev/Compiler.java
@@ -173,7 +173,8 @@
 
         if (options.isValidateOnly()) {
           if (!Precompile.validate(logger, options, module,
-              options.getGenDir(), compilerWorkDir)) {
+              options.getGenDir(), compilerWorkDir,
+              options.getDumpSignatureFile())) {
             return false;
           }
         } else {
@@ -182,7 +183,8 @@
               + moduleName);
 
           Precompilation precompilation = Precompile.precompile(logger,
-              options, module, options.getGenDir(), compilerWorkDir);
+              options, module, options.getGenDir(), compilerWorkDir,
+              options.getDumpSignatureFile());
 
           if (precompilation == null) {
             return false;
diff --git a/dev/core/src/com/google/gwt/dev/GWTCompiler.java b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
index 5f9e30e..a2d8be7 100644
--- a/dev/core/src/com/google/gwt/dev/GWTCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/GWTCompiler.java
@@ -175,7 +175,8 @@
 
         if (options.isValidateOnly()) {
           if (!Precompile.validate(logger, options, module,
-              options.getGenDir(), compilerWorkDir)) {
+              options.getGenDir(), compilerWorkDir,
+              options.getDumpSignatureFile())) {
             return false;
           }
         } else {
@@ -184,7 +185,8 @@
               + moduleName);
 
           Precompilation precompilation = Precompile.precompile(logger,
-              options, module, options.getGenDir(), compilerWorkDir);
+              options, module, options.getGenDir(), compilerWorkDir,
+              options.getDumpSignatureFile());
 
           if (precompilation == null) {
             return false;
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index b477267..fb64d94 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -46,13 +46,15 @@
 import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableClassMetadata;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableRunAsync;
-import com.google.gwt.dev.util.arg.ArgHandlerDraftCompile;
 import com.google.gwt.dev.util.arg.ArgHandlerDisableUpdateCheck;
+import com.google.gwt.dev.util.arg.ArgHandlerDraftCompile;
+import com.google.gwt.dev.util.arg.ArgHandlerDumpSignatures;
 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 com.google.gwt.dev.util.arg.OptionDisableUpdateCheck;
+import com.google.gwt.dev.util.arg.OptionDumpSignatures;
 import com.google.gwt.dev.util.arg.OptionGenDir;
 import com.google.gwt.dev.util.arg.OptionValidateOnly;
 
@@ -74,7 +76,8 @@
    * The set of options for the precompiler.
    */
   public interface PrecompileOptions extends JJSOptions, CompileTaskOptions,
-      OptionGenDir, OptionValidateOnly, OptionDisableUpdateCheck {
+      OptionGenDir, OptionValidateOnly, OptionDisableUpdateCheck,
+      OptionDumpSignatures {
   }
 
   static class ArgProcessor extends CompileArgProcessor {
@@ -89,6 +92,7 @@
       registerHandler(new ArgHandlerDisableRunAsync(options));
       registerHandler(new ArgHandlerDraftCompile(options));
       registerHandler(new ArgHandlerDisableUpdateCheck(options));
+      registerHandler(new ArgHandlerDumpSignatures(options));
     }
 
     @Override
@@ -100,6 +104,7 @@
   static class PrecompileOptionsImpl extends CompileTaskOptionsImpl implements
       PrecompileOptions {
     private boolean disableUpdateCheck;
+    private File dumpFile;
     private File genDir;
     private final JJSOptionsImpl jjsOptions = new JJSOptionsImpl();
     private boolean validateOnly;
@@ -117,10 +122,15 @@
       jjsOptions.copyFrom(other);
 
       setDisableUpdateCheck(other.isUpdateCheckDisabled());
+      setDumpSignatureFile(other.getDumpSignatureFile());
       setGenDir(other.getGenDir());
       setValidateOnly(other.isValidateOnly());
     }
 
+    public File getDumpSignatureFile() {
+      return dumpFile;
+    }
+
     public File getGenDir() {
       return genDir;
     }
@@ -177,6 +187,10 @@
       jjsOptions.setDraftCompile(draft);
     }
 
+    public void setDumpSignatureFile(File dumpFile) {
+      this.dumpFile = dumpFile;
+    }
+
     public void setEnableAssertions(boolean enableAssertions) {
       jjsOptions.setEnableAssertions(enableAssertions);
     }
@@ -317,9 +331,14 @@
    */
   public static Precompilation precompile(TreeLogger logger,
       JJSOptions jjsOptions, ModuleDef module, File genDir,
-      File generatorResourcesDir) {
+      File generatorResourcesDir, File dumpSignatureFile) {
     try {
       CompilationState compilationState = module.getCompilationState(logger);
+      if (dumpSignatureFile != null) {
+        // Dump early to avoid generated types.
+        SignatureDumper.dumpSignatures(logger, module.getTypeOracle(logger),
+            dumpSignatureFile);
+      }
 
       String[] declEntryPts = module.getEntryPointTypeNames();
       if (declEntryPts.length == 0) {
@@ -377,9 +396,15 @@
    * @param generatorResourcesDir required directory to dump generator resources
    */
   public static boolean validate(TreeLogger logger, JJSOptions jjsOptions,
-      ModuleDef module, File genDir, File generatorResourcesDir) {
+      ModuleDef module, File genDir, File generatorResourcesDir,
+      File dumpSignatureFile) {
     try {
       CompilationState compilationState = module.getCompilationState(logger);
+      if (dumpSignatureFile != null) {
+        // Dump early to avoid generated types.
+        SignatureDumper.dumpSignatures(logger, module.getTypeOracle(logger),
+            dumpSignatureFile);
+      }
 
       String[] declEntryPts = module.getEntryPointTypeNames();
       String[] additionalRootTypes = null;
@@ -435,7 +460,7 @@
         TreeLogger branch = logger.branch(TreeLogger.INFO,
             "Validating compilation " + module.getName());
         if (!validate(branch, options, module, options.getGenDir(),
-            compilerWorkDir)) {
+            compilerWorkDir, options.getDumpSignatureFile())) {
           branch.log(TreeLogger.ERROR, "Validation failed");
           return false;
         }
@@ -444,7 +469,8 @@
         TreeLogger branch = logger.branch(TreeLogger.INFO,
             "Precompiling module " + module.getName());
         Precompilation precompilation = precompile(branch, options, module,
-            options.getGenDir(), compilerWorkDir);
+            options.getGenDir(), compilerWorkDir,
+            options.getDumpSignatureFile());
         if (precompilation == null) {
           branch.log(TreeLogger.ERROR, "Precompilation failed");
           return false;
diff --git a/dev/core/src/com/google/gwt/dev/SignatureDumper.java b/dev/core/src/com/google/gwt/dev/SignatureDumper.java
new file mode 100644
index 0000000..51164db
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/SignatureDumper.java
@@ -0,0 +1,121 @@
+/*
+ * 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.typeinfo.JAbstractMethod;
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.core.ext.typeinfo.JConstructor;
+import com.google.gwt.core.ext.typeinfo.JField;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.core.ext.typeinfo.JParameter;
+import com.google.gwt.core.ext.typeinfo.TypeOracle;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+class SignatureDumper {
+
+  public static void dumpSignatures(TreeLogger logger, TypeOracle typeOracle,
+      File outFile) {
+    try {
+      PrintWriter out = new PrintWriter(new FileWriter(outFile));
+      out.println("# Contains all signatures dumped from the GWT compiler");
+      out.println("FileVersion 1");
+      out.println("GwtVersion " + About.GWT_VERSION_NUM);
+      out.print(dumpAllSignatures(typeOracle));
+      out.close();
+      logger.log(TreeLogger.INFO, "Signatures dumped into " + outFile, null);
+    } catch (IOException ex) {
+      logger.log(TreeLogger.ERROR, "Could not dump signatures: IOError", null);
+    }
+  }
+
+  private static void addMethods(JAbstractMethod[] methods, StringBuilder result) {
+    for (JAbstractMethod currentMeth : methods) {
+      if (currentMeth.isConstructor() != null) {
+        result.append(" method <init>");
+      } else if (currentMeth.isMethod() != null) {
+        result.append(" method ");
+        if (currentMeth.isMethod().isStatic()) {
+          result.append("static ");
+        }
+        result.append(currentMeth.getName());
+      } else {
+        continue;
+      }
+      result.append(" (");
+      for (JParameter currentParam : currentMeth.getParameters()) {
+        result.append(currentParam.getType().getJNISignature());
+      }
+      result.append(')');
+      if (currentMeth.isConstructor() != null) {
+        result.append('V');
+      } else {
+        result.append(((JMethod) currentMeth).getReturnType().getJNISignature());
+      }
+      result.append('\n');
+    }
+  }
+
+  /**
+   * Dumps the signatures within this typeOracle. Singatures may appear multiple
+   * times.
+   */
+  private static String dumpAllSignatures(TypeOracle typeOracle) {
+    StringBuilder result = new StringBuilder();
+    for (JClassType current : typeOracle.getTypes()) {
+      if (current.isInterface() != null) {
+        result.append("interface ");
+      } else {
+        result.append("class ");
+      }
+      result.append(current.getJNISignature());
+      if (current.getSuperclass() != null) {
+        result.append(" extends ");
+        result.append(current.getSuperclass().getJNISignature());
+      }
+      for (JClassType currentInterface : current.getImplementedInterfaces()) {
+        result.append(" implements ");
+        result.append(currentInterface.getJNISignature());
+      }
+      result.append('\n');
+
+      result.append(" method static <clinit> ()V\n");
+      JConstructor[] constructors = current.getConstructors();
+      if (constructors.length == 0) {
+        result.append(" method <init> ()V\n");
+      } else {
+        addMethods(constructors, result);
+      }
+      addMethods(current.getMethods(), result);
+      for (JField currentField : current.getFields()) {
+        result.append(" field ");
+        if (currentField.isStatic()) {
+          result.append("static ");
+        }
+        result.append(currentField.getName());
+        result.append(' ');
+        result.append(currentField.getType().getJNISignature());
+        result.append('\n');
+      }
+    }
+    return result.toString();
+  }
+
+}
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDumpSignatures.java b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDumpSignatures.java
new file mode 100644
index 0000000..620db3b
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/ArgHandlerDumpSignatures.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2009 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.ArgHandlerFile;
+
+import java.io.File;
+
+/**
+ * Argument handler for processing the script style flag.
+ */
+public final class ArgHandlerDumpSignatures extends ArgHandlerFile {
+
+  private final OptionDumpSignatures option;
+
+  public ArgHandlerDumpSignatures(OptionDumpSignatures option) {
+    this.option = option;
+  }
+
+  public String getPurpose() {
+    return "Dump the singatures all loaded types and their members";
+  }
+
+  public String getTag() {
+    return "-dumpSignatures";
+  }
+
+  public String[] getTagArgs() {
+    return new String[] {"style"};
+  }
+
+  @Override
+  public boolean isUndocumented() {
+    return true;
+  }
+
+  @Override
+  public void setFile(File file) {
+    option.setDumpSignatureFile(file);
+  }
+}
\ No newline at end of file
diff --git a/dev/core/src/com/google/gwt/dev/util/arg/OptionDumpSignatures.java b/dev/core/src/com/google/gwt/dev/util/arg/OptionDumpSignatures.java
new file mode 100644
index 0000000..3d9b9ff
--- /dev/null
+++ b/dev/core/src/com/google/gwt/dev/util/arg/OptionDumpSignatures.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2009 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 OptionDumpSignatures {
+
+  /**
+   * Returns the signature dump file.
+   */
+  File getDumpSignatureFile();
+
+  /**
+   * Sets the signature dump file.
+   */
+  void setDumpSignatureFile(File dumpFile);
+}
diff --git a/dev/core/src/com/google/gwt/util/tools/ArgHandlerFile.java b/dev/core/src/com/google/gwt/util/tools/ArgHandlerFile.java
new file mode 100644
index 0000000..7aad819
--- /dev/null
+++ b/dev/core/src/com/google/gwt/util/tools/ArgHandlerFile.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.util.tools;
+
+import java.io.File;
+
+/**
+ * Argument handler for arguments that are directories.  
+ */
+public abstract class ArgHandlerFile extends ArgHandler {
+
+  public String[] getDefaultArgs() {
+    return null;
+  }
+
+  public abstract String getPurpose();
+
+  public abstract String getTag();
+
+  public String[] getTagArgs() {
+    return new String[]{"file"};
+  }
+
+  public int handle(String[] args, int startIndex) {
+    if (startIndex + 1 < args.length) {
+      setFile(new File(args[startIndex + 1]));
+      return 1;
+    }
+
+    System.err.println(getTag()
+      + " should be followed by the name of a file");
+    return -1;
+  }
+
+  public abstract void setFile(File file);
+
+}