Add sourcemap support to super dev mode. Source files work in Chrome canary
for everything except for super source. Also, enable super dev mode for
TourGuide's gin sample.

(This is a cleaned-up version of cromwellian's patch for the GWT summit.)

Review by: cromwellian@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10836 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java b/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
index 5d4441b..8e90c2c 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsReportGenerationVisitor.java
@@ -18,7 +18,10 @@
 import com.google.gwt.core.ext.soyc.Range;
 import com.google.gwt.dev.jjs.HasSourceInfo;
 import com.google.gwt.dev.jjs.SourceInfo;
+import com.google.gwt.dev.jjs.SourceOrigin;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
 import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
+import com.google.gwt.dev.js.ast.JsName;
 import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.util.TextOutput;
 
@@ -42,23 +45,33 @@
   }
 
   @Override
-  public Map<Range, SourceInfo> getSourceInfoMap() {
-    return Collections.unmodifiableMap(sourceInfoMap);
+  protected <T extends JsVisitable> T generateAndBill(T node, JsName nameToBillTo) {
+
+    if (!(node instanceof HasSourceInfo)) {
+      return super.generateAndBill(node, nameToBillTo);
+    }
+
+    // Remember the position before generating the JavaScript.
+    int beforePosition = out.getPosition();
+    int beforeLine = out.getLine();
+    int beforeColumn = out.getColumn();
+
+    // Write some JavaScript (changing the position).
+    T toReturn = super.generateAndBill(node, nameToBillTo);
+
+    Range javaScriptRange = new Range(beforePosition, out.getPosition(),
+        beforeLine, beforeColumn, out.getLine(), out.getColumn());
+
+    SourceInfo defaultTarget = ((HasSourceInfo) node).getSourceInfo();
+    SourceInfo newTarget = findTarget(nameToBillTo, defaultTarget);
+    sourceInfoMap.put(javaScriptRange, newTarget);
+
+    return toReturn;
   }
 
   @Override
-  protected <T extends JsVisitable> T doAccept(T node) {
-    boolean addEntry = node instanceof HasSourceInfo;
-    int start = addEntry ? out.getPosition() : 0;
-    int sLine = out.getLine();
-    int sCol = out.getColumn();
-    T toReturn = super.doAccept(node);
-    if (addEntry) {
-      SourceInfo info = ((HasSourceInfo) node).getSourceInfo();
-      sourceInfoMap.put(new Range(start, out.getPosition(),
-          sLine, sCol, out.getLine(), out.getColumn()), info);
-    }
-    return toReturn;
+  public Map<Range, SourceInfo> getSourceInfoMap() {
+    return Collections.unmodifiableMap(sourceInfoMap);
   }
 
   @Override
@@ -75,4 +88,39 @@
       doAccept(t);
     }
   }
+
+  /**
+   * Finds the Java filename and line number that we want in the source map.
+   * (This needs to be a relative path that makes sense as a URL.)
+   */
+  private SourceInfo findTarget(JsName nameToBillTo, SourceInfo defaultTarget) {
+    String newFilename = findTargetFile(nameToBillTo, defaultTarget.getFileName());
+
+    if (newFilename == defaultTarget.getFileName()) {
+      return defaultTarget;
+    } else {
+      return SourceOrigin.create(defaultTarget.getStartLine(), newFilename);
+    }
+  }
+
+  /**
+   * Finds the name of the Java file that we want to put in the source map.
+   */
+  private String findTargetFile(JsName nameToBillTo, String defaultFilename) {
+    // For the filename, we really want the path passed to ResourceLoader.getResource().
+    // But for now, fake it based on the type name.
+    // TODO(skybrian): fix
+
+    JDeclaredType type = getDirectlyEnclosingType(nameToBillTo);
+    if (type == null) {
+      return defaultFilename;
+    }
+
+    // remove inner classes
+    while (type.getEnclosingType() != null) {
+      type = type.getEnclosingType();
+    }
+
+    return type.getName().replace('.', '/') + ".java";
+  }
 }
diff --git a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
index f123b0f..e471cc3 100644
--- a/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
+++ b/dev/core/src/com/google/gwt/dev/js/JsSourceGenerationVisitorWithSizeBreakdown.java
@@ -18,6 +18,8 @@
 import com.google.gwt.core.ext.soyc.Range;
 import com.google.gwt.dev.jjs.SourceInfo;
 import com.google.gwt.dev.jjs.ast.JClassType;
+import com.google.gwt.dev.jjs.ast.JDeclaredType;
+import com.google.gwt.dev.jjs.ast.JField;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.impl.FragmentExtractor;
 import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
@@ -28,8 +30,8 @@
 import com.google.gwt.dev.js.ast.JsProgramFragment;
 import com.google.gwt.dev.js.ast.JsSeedIdOf;
 import com.google.gwt.dev.js.ast.JsStatement;
-import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.js.ast.JsVars.JsVar;
+import com.google.gwt.dev.js.ast.JsVisitable;
 import com.google.gwt.dev.util.TextOutput;
 import com.google.gwt.dev.util.collect.HashMap;
 
@@ -43,8 +45,8 @@
 public class JsSourceGenerationVisitorWithSizeBreakdown extends
     JsSourceGenerationVisitor {
 
-  private final JavaToJavaScriptMap map;
-  private JsName nameToBillTo;
+  private JavaToJavaScriptMap map;
+  private JsName billedAncestor; // non-null when an ancestor is also being billed
   private TextOutput out;
   private final Map<JsName, Integer> sizeMap = new HashMap<JsName, Integer>();
 
@@ -89,19 +91,9 @@
   }
 
   @Override
-  protected <T extends JsVisitable> T doAccept(T node) {
-    JsName newName = nameToBillTo(node);
-    if (newName == null) {
-      return super.doAccept(node);
-    } else {
-      JsName oldName = nameToBillTo;
-      nameToBillTo = newName;
-      int start = out.getPosition();
-      T retValue = super.doAccept(node);
-      billChars(nameToBillTo, out.getPosition() - start);
-      nameToBillTo = oldName;
-      return retValue;
-    }
+  protected final <T extends JsVisitable> T doAccept(T node) {
+    JsName newName = nameToBillTo(node, billedAncestor != null);
+    return generateAndBill(node, newName);
   }
 
   @Override
@@ -119,6 +111,48 @@
     }
   }
 
+  /**
+   * Generate some JavaScript and bill the number of characters generated to the given name.
+   */
+  protected <T extends JsVisitable> T generateAndBill(T node, JsName nameToBillTo) {
+    if (nameToBillTo == null) {
+      return super.doAccept(node);
+    } else {
+      int start = out.getPosition();
+
+      JsName savedAncestor = billedAncestor;
+      billedAncestor = nameToBillTo;
+      T retValue = super.doAccept(node);
+      billedAncestor = savedAncestor;
+
+      billChars(nameToBillTo, out.getPosition() - start);
+      return retValue;
+    }
+  }
+
+  protected JDeclaredType getDirectlyEnclosingType(JsName nameToBillTo) {
+    if (nameToBillTo == null) {
+      return null;
+    }
+
+    JDeclaredType type = map.nameToType(nameToBillTo);
+    if (type != null) {
+      return type;
+    }
+
+    JMethod method = map.nameToMethod(nameToBillTo);
+    if (method != null) {
+      return method.getEnclosingType();
+    }
+
+    JField field = map.nameToField(nameToBillTo);
+    if (field != null) {
+      return field.getEnclosingType();
+    }
+
+    return null;
+  }
+
   private void billChars(JsName nameToBillTo, int chars) {
     Integer oldSize = sizeMap.get(nameToBillTo);
     if (oldSize == null) {
@@ -128,10 +162,10 @@
   }
 
   /**
-   * If parameter is JsVisitable, javac version sun jdk1.6.0 complains about
-   * incompatible types.
+   * Returns the type, function, or variable name where this node's character count
+   * should be added, or null to bill to nobody.
    */
-  private JsName nameToBillTo(JsVisitable node) {
+  private JsName nameToBillTo(JsVisitable node, boolean isAncestorBilled) {
     if (node instanceof JsStatement) {
       JsStatement stat = (JsStatement) node;
       JClassType type = map.typeForStatement(stat);
@@ -143,12 +177,11 @@
       if (method != null) {
         return map.nameForMethod(method);
       }
-    }
 
-    if (node instanceof JsVar) {
-      if (nameToBillTo == null) {
-        return ((JsVar) node).getName();
-      }
+      return null;
+
+    } else if (node instanceof JsVar) {
+      return isAncestorBilled ? null : ((JsVar) node).getName(); // handle top-level vars
     }
 
     return null;
diff --git a/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml b/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml
index a829874..3384a1b 100644
--- a/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml
+++ b/user/src/com/google/gwt/core/CoreWithUserAgent.gwt.xml
@@ -51,8 +51,6 @@
     </none> 
   </set-property>
 
-<set-property name="compiler.useSourceMaps" value="false"/>
-
   <!-- Utility class to query if source maps are enabled, mainly for testing -->
   <replace-with class="com.google.gwt.core.client.impl.SourceMapProperty.SourceMapEnabled">
     <when-type-is class="com.google.gwt.core.client.impl.SourceMapProperty.SourceMapImpl"/>