Author: amitman...@google.com
Date: Wed Feb  4 14:50:42 2009
New Revision: 4627

Added:
     
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameFinderTest.java
Modified:
    releases/1.6/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
     
releases/1.6/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
     
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameComparatorTest.java
     
releases/1.6/dev/core/test/com/google/gwt/dev/shell/GeneratedClassnameTest.java
    releases/1.6/user/build.xml
    releases/1.6/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java
     
releases/1.6/user/test/com/google/gwt/dev/shell/rewrite/client/EmmaClassLoadingTest.java

Log:
This patch expands Gwt's ability to successfully create classMappings,  
necessary when running Emma. The following types of classes
can now be handled correctly:
(a) A top level class that is not the main type in a java file (e.g.,  
Test$1Foo produced from Bar.java)
(b) A class that is nested multiple-levels deep (e.g., Test$Foo$1Bar)
(c) An anonmyous class that extends a named local class (e.g., Test$1Foo)

Also adds a test target to enable running all hosted mode tests in Emma  
mode. All tests pass except CoverageTest.java that fails
because of a bug in javac in both OpenJDK and Sun's Java6.

Patch by: amitmanjhi
Review by: jat (desk review)



Modified:  
releases/1.6/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java
==============================================================================
--- releases/1.6/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java     
 
(original)
+++ releases/1.6/dev/core/src/com/google/gwt/dev/javac/CompilationUnit.java     
 
Wed Feb  4 14:50:42 2009
@@ -15,11 +15,13 @@
   */
  package com.google.gwt.dev.javac;

+import com.google.gwt.core.ext.TreeLogger;
  import com.google.gwt.dev.asm.ClassReader;
  import com.google.gwt.dev.asm.Opcodes;
  import com.google.gwt.dev.asm.commons.EmptyVisitor;
  import com.google.gwt.dev.jdt.TypeRefVisitor;
  import com.google.gwt.dev.shell.CompilingClassLoader;
+import com.google.gwt.dev.util.Util;

  import org.eclipse.jdt.core.compiler.CategorizedProblem;
  import org.eclipse.jdt.internal.compiler.ASTVisitor;
@@ -30,6 +32,9 @@
  import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
  import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;

+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
@@ -46,25 +51,129 @@
   */
  public abstract class CompilationUnit {

-  static class AnonymousClassVisitor extends EmptyVisitor {
-    /*
-     * array of classNames of inner clases that aren't synthetic classes.
-     */
-    List<String> classNames = new ArrayList<String>();
+  /**
+   * Encapsulates the functionality to find all nested classes of this  
class
+   * that have compiler-generated names. All class bytes are loaded from  
the
+   * disk and then analyzed using ASM.
+   */
+  static class GeneratedClassnameFinder {
+    private static class AnonymousClassVisitor extends EmptyVisitor {
+      /*
+       * array of classNames of inner clases that aren't synthetic classes.
+       */
+      List<String> classNames = new ArrayList<String>();

-    public List<String> getInnerClassNames() {
-      return classNames;
+      public List<String> getInnerClassNames() {
+        return classNames;
+      }
+
+      @Override
+      public void visitInnerClass(String name, String outerName,
+          String innerName, int access) {
+        if ((access & Opcodes.ACC_SYNTHETIC) == 0) {
+          classNames.add(name);
+        }
+      }
      }

-    @Override
-    public void visitInnerClass(String name, String outerName,
-        String innerName, int access) {
-      if ((access & Opcodes.ACC_SYNTHETIC) == 0) {
-        classNames.add(name);
+    private final List<String> classesToScan;
+    private final TreeLogger logger;
+    private final String mainClass;
+    private String mainUrlBase = null;
+
+    GeneratedClassnameFinder(TreeLogger logger, String mainClass) {
+      assert mainClass != null;
+      this.mainClass = mainClass;
+      classesToScan = new ArrayList<String>();
+      classesToScan.add(mainClass);
+      this.logger = logger;
+    }
+
+    List<String> getClassNames() {
+      // using a list because presumably there will not be many generated
+      // classes
+      List<String> allGeneratedClasses = new ArrayList<String>();
+      for (int i = 0; i < classesToScan.size(); i++) {
+        String lookupName = classesToScan.get(i);
+        byte classBytes[] = getClassBytes(lookupName);
+        if (classBytes == null) {
+          /*
+           * Weird case: javac might generate a name and reference the  
class in
+           * the bytecode but decide later that the class is unnecessary.  
In the
+           * bytecode, a null is passed for the class.
+           */
+          continue;
+        }
+
+        /*
+         * Add the class to the list only if it can be loaded to get  
around the
+         * javac weirdness issue where javac refers a class but does not
+         * generate it.
+         */
+        if (CompilingClassLoader.isClassnameGenerated(lookupName)
+            && !allGeneratedClasses.contains(lookupName)) {
+          allGeneratedClasses.add(lookupName);
+        }
+        AnonymousClassVisitor cv = new AnonymousClassVisitor();
+        new ClassReader(classBytes).accept(cv, 0);
+        List<String> innerClasses = cv.getInnerClassNames();
+        for (String innerClass : innerClasses) {
+          // The innerClass has to be an inner class of the lookupName
+          if (!innerClass.startsWith(mainClass + "$")) {
+            continue;
+          }
+          /*
+           * TODO (amitmanjhi): consider making this a Set if necessary for
+           * performance
+           */
+          // add the class to classes
+          if (!classesToScan.contains(innerClass)) {
+            classesToScan.add(innerClass);
+          }
+        }
        }
+      Collections.sort(allGeneratedClasses, new  
GeneratedClassnameComparator());
+      return allGeneratedClasses;
      }
-  }

+    /*
+     * Load classBytes from disk. Check if the classBytes are loaded from  
the
+     * same location as the location of the mainClass.
+     */
+    private byte[] getClassBytes(String slashedName) {
+      URL url = Thread.currentThread().getContextClassLoader().getResource(
+          slashedName + ".class");
+      if (url == null) {
+        logger.log(TreeLogger.DEBUG, "Unable to find " + slashedName
+            + " on the classPath");
+        return null;
+      }
+      String urlStr = url.toExternalForm();
+      if (slashedName.equals(mainClass)) {
+        // initialize the mainUrlBase for later use.
+        mainUrlBase = urlStr.substring(0, urlStr.lastIndexOf('/'));
+      } else {
+        assert mainUrlBase != null;
+        if (!mainUrlBase.equals(urlStr.substring(0,  
urlStr.lastIndexOf('/')))) {
+          logger.log(TreeLogger.DEBUG, "Found " + slashedName + " at " +  
urlStr
+              + " The base location is different from  that of " +  
mainUrlBase
+              + " Not loading");
+          return null;
+        }
+      }
+
+      // url != null, we found it on the class path.
+      try {
+        URLConnection conn = url.openConnection();
+        return Util.readURLConnectionAsBytes(conn);
+      } catch (IOException ignored) {
+        logger.log(TreeLogger.DEBUG, "Unable to load " + urlStr
+            + ", in trying to load " + slashedName);
+        // Fall through.
+      }
+      return null;
+    }
+  }
    /**
     * Tracks the state of a compilation unit through the compile and  
recompile
     * process.
@@ -167,26 +276,31 @@
    private State state = State.FRESH;

    /*
-   * Check if the unit has one or more anonymous classes. 'javac' below  
refers
-   * to the compiler that was used to compile the java files on disk.  
Returns
-   * true if our heuristic for constructing the anonymous class mappings  
worked.
+   * Check if the unit has one or more classes with generated  
names. 'javac'
+   * below refers to the compiler that was used to compile the java files  
on
+   * disk. Returns true if our heuristic for constructing the anonymous  
class
+   * mappings worked.
     */
-  public boolean constructAnonymousClassMappings(byte classBytes[]) {
+  public boolean constructAnonymousClassMappings(TreeLogger logger) {
      // map from the name in javac to the name in jdt
      anonymousClassMap = new HashMap<String, String>();
-    List<String> javacClasses = getJavacClassNames(classBytes);
-    List<String> jdtClasses = getJdtClassNames();
-    if (javacClasses.size() == jdtClasses.size()) {
+    for (String topLevelClass : getTopLevelClasses()) {
+      // Generate a mapping for each top-level class separately
+      List<String> javacClasses = new GeneratedClassnameFinder(logger,
+          topLevelClass).getClassNames();
+      List<String> jdtClasses = getJdtClassNames(topLevelClass);
+      if (javacClasses.size() != jdtClasses.size()) {
+        anonymousClassMap = Collections.emptyMap();
+        return false;
+      }
        int size = javacClasses.size();
        for (int i = 0; i < size; i++) {
          if (!javacClasses.get(i).equals(jdtClasses.get(i))) {
            anonymousClassMap.put(javacClasses.get(i), jdtClasses.get(i));
          }
        }
-      return true;
      }
-    anonymousClassMap = Collections.emptyMap();
-    return false;
+    return true;
    }

    public boolean createdClassMapping() {
@@ -383,30 +497,26 @@
      }
    }

-  private List<String> getJavacClassNames(byte classBytes[]) {
-    AnonymousClassVisitor cv = new AnonymousClassVisitor();
-    new ClassReader(classBytes).accept(cv, 0);
-    List<String> classNames = cv.getInnerClassNames();
-    List<String> namesToRemove = new ArrayList<String>();
-    for (String className : classNames) {
-      if (!CompilingClassLoader.isClassnameGenerated(className)) {
-        namesToRemove.add(className);
+  private List<String> getJdtClassNames(String topLevelClass) {
+    List<String> classNames = new ArrayList<String>();
+    for (CompiledClass cc : getCompiledClasses()) {
+      if (isAnonymousClass(cc)
+          && cc.getBinaryName().startsWith(topLevelClass + "$")) {
+        classNames.add(cc.getBinaryName());
        }
      }
-    classNames.removeAll(namesToRemove);
      Collections.sort(classNames, new GeneratedClassnameComparator());
      return classNames;
    }

-  private List<String> getJdtClassNames() {
-    List<String> classNames = new ArrayList<String>();
+  private List<String> getTopLevelClasses() {
+    List<String> topLevelClasses = new ArrayList<String>();
      for (CompiledClass cc : getCompiledClasses()) {
-      if (isAnonymousClass(cc)) {
-        classNames.add(cc.getBinaryName());
+      if (cc.getEnclosingClass() == null) {
+        topLevelClasses.add(cc.binaryName);
        }
      }
-    Collections.sort(classNames, new GeneratedClassnameComparator());
-    return classNames;
+    return topLevelClasses;
    }

    /**

Modified:  
releases/1.6/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
==============================================================================
---  
releases/1.6/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java    
 
(original)
+++  
releases/1.6/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java    
 
Wed Feb  4 14:50:42 2009
@@ -361,7 +361,7 @@
     */
    private static byte[] javaScriptHostBytes;

-  private static final Pattern GENERATED_CLASSNAME_PATTERN =  
Pattern.compile(".+\\$\\d+(\\$.*)?");
+  private static final Pattern GENERATED_CLASSNAME_PATTERN =  
Pattern.compile(".+\\$\\d.*");

    static {
      for (Class<?> c : BRIDGE_CLASSES) {
@@ -386,9 +386,10 @@

    /**
     * Checks if the class names is generated. Accepts any classes whose  
names
-   * match .+$\d+($.*)? (handling named classes within anonymous classes).
-   * Checks if the class or any of its enclosing classes are anonymous or
-   * synthetic.
+   * match .+$\d.* (handling named classes within anonymous classes and
+   * multiple named classes of the same name in a class, but in different
+   * methods). Checks if the class or any of its enclosing classes are  
anonymous
+   * or synthetic.
     * <p>
     * If new compilers have different conventions for anonymous and  
synthetic
     * classes, this code needs to be updated.
@@ -670,7 +671,7 @@
          lookupClassName);

      CompilationUnit unit = (compiledClass == null)
-        ? getUnitForClassName(className) : compiledClass.getUnit();
+        ? getUnitForClassName(lookupClassName) : compiledClass.getUnit();
      if (emmaAvailable) {
        /*
         * build the map for anonymous classes. Do so only if unit has  
anonymous
@@ -682,20 +683,12 @@
        if (unit != null && !unit.isSuperSource() &&  
unit.hasAnonymousClasses()
            && jsniMethods != null && jsniMethods.size() > 0
            && !unit.createdClassMapping()) {
-        String mainLookupClassName = unit.getTypeName().replace('.', '/');
-        byte mainClassBytes[] = emmaStrategy.getEmmaClassBytes(null,
-            mainLookupClassName, 0);
-        if (mainClassBytes != null) {
-          if (!unit.constructAnonymousClassMappings(mainClassBytes)) {
-            logger.log(TreeLogger.ERROR,
-                "Our heuristic for mapping anonymous classes between  
compilers "
-                    + "failed. Unsafe to continue because the wrong jsni  
code "
-                    + "could end up running. className = " + className);
-            return null;
-          }
-        } else {
-          logger.log(TreeLogger.ERROR, "main class bytes is null for unit  
= "
-              + unit + ", mainLookupClassName = " + mainLookupClassName);
+        if (!unit.constructAnonymousClassMappings(logger)) {
+          logger.log(TreeLogger.ERROR,
+              "Our heuristic for mapping anonymous classes between  
compilers "
+                  + "failed. Unsafe to continue because the wrong jsni  
code "
+                  + "could end up running. className = " + className);
+          return null;
          }
        }
      }
@@ -717,7 +710,8 @@
         * find it on disk. Typically this is a synthetic class added by the
         * compiler.
         */
-      if (typeHasCompilationUnit(className) &&  
isClassnameGenerated(className)) {
+      if (typeHasCompilationUnit(lookupClassName)
+          && isClassnameGenerated(className)) {
          /*
           * modification time = 0 ensures that whatever is on the disk is  
always
           * loaded.
@@ -758,17 +752,19 @@
    /**
     * Returns the compilationUnit corresponding to the className. For nested
     * classes, the unit corresponding to the top level type is returned.
+   *
+   * Since a file might have several top-level types, search using  
classFileMap.
     */
    private CompilationUnit getUnitForClassName(String className) {
      String mainTypeName = className;
      int index = mainTypeName.length();
-    CompilationUnit unit = null;
-    while (unit == null && index != -1) {
+    CompiledClass cc = null;
+    while (cc == null && index != -1) {
        mainTypeName = mainTypeName.substring(0, index);
-      unit = compilationState.getCompilationUnitMap().get(mainTypeName);
+      cc = compilationState.getClassFileMap().get(mainTypeName);
        index = mainTypeName.lastIndexOf('$');
      }
-    return unit;
+    return cc == null ? null : cc.getUnit();
    }

    private void injectJsniMethods(CompilationUnit unit) {

Modified:  
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameComparatorTest.java
==============================================================================
---  
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameComparatorTest.java
        
(original)
+++  
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameComparatorTest.java
        
Wed Feb  4 14:50:42 2009
@@ -1,3 +1,18 @@
+/*
+ * 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.javac;

  import junit.framework.TestCase;
@@ -38,8 +53,21 @@
    }

    public void testMixedNames() {
-    String original[] = {"Foo", "Foo$1", "Foo$1xyz", "Foo$2", "Foo$xyz"};
-    String expected[] = {"Foo", "Foo$1", "Foo$2", "Foo$1xyz", "Foo$xyz"};
+    String original[] = {
+        "Foo", "Foo$1", "Foo$1Bar", "Foo$2Bar", "Foo$2", "Foo$xyz"};
+    String expected[] = {
+        "Foo", "Foo$1", "Foo$2", "Foo$1Bar", "Foo$2Bar", "Foo$xyz"};
+    Arrays.sort(original, new GeneratedClassnameComparator());
+    for (int i = 0; i < original.length; i++) {
+      assertEquals("index = " + i, expected[i], original[i]);
+    }
+  }
+
+  public void testMultipleToplevelClasses() {
+    String original[] = {
+        "Foo$1", "Foo$2", "Bar$1", "Bar$3", "Foo$2$1", "Bar$2$1"};
+    String expected[] = {
+        "Bar$1", "Bar$3", "Foo$1", "Foo$2", "Bar$2$1", "Foo$2$1"};
      Arrays.sort(original, new GeneratedClassnameComparator());
      for (int i = 0; i < original.length; i++) {
        assertEquals("index = " + i, expected[i], original[i]);

Added:  
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameFinderTest.java
==============================================================================
--- (empty file)
+++  
releases/1.6/dev/core/test/com/google/gwt/dev/javac/GeneratedClassnameFinderTest.java
    
Wed Feb  4 14:50:42 2009
@@ -0,0 +1,371 @@
+/*
+ * 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.javac;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.javac.CompilationUnit.GeneratedClassnameFinder;
+import com.google.gwt.dev.util.log.PrintWriterTreeLogger;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * Test-cases to check that we indeed obtain the correct list of nested  
types
+ * with generated classNames by examining bytecodes using ASM.
+ *
+ */
+public class GeneratedClassnameFinderTest extends TestCase {
+  enum EnumClass {
+    A, B, C,
+  }
+
+  static class MainClass {
+    static class NestedClass {
+      void foo() {
+        TestInterface c = new TestInterface() {
+          public void foo() {
+          }
+        };
+        EnumClass et = EnumClass.A;
+        switch (et) {
+          case A:
+            break;
+        }
+        TestInterface d = new TestInterface() {
+          public void foo() {
+          }
+        };
+      }
+    }
+
+    void foo() {
+      TestInterface a = new TestInterface() {
+        public void foo() {
+        }
+      };
+      EnumClass et = EnumClass.A;
+      switch (et) {
+        case A:
+          break;
+      }
+      TestInterface b = new TestInterface() {
+        public void foo() {
+        }
+      };
+    }
+  }
+  interface TestInterface {
+    void foo();
+  }
+
+  static final TreeLogger logger = new PrintWriterTreeLogger();
+
+  public void test() {
+    String mainClassName = this.getClass().getName().replace('.', '/');
+    assertEquals(
+        4,
+        new GeneratedClassnameFinder(logger,  
mainClassName).getClassNames().size());
+    assertEquals(0, new GeneratedClassnameFinder(logger, mainClassName
+        + "$EnumClass").getClassNames().size());
+    assertEquals(0, new GeneratedClassnameFinder(logger, mainClassName
+        + "$TestInterface").getClassNames().size());
+    assertEquals(4, new GeneratedClassnameFinder(logger, mainClassName
+        + "$MainClass").getClassNames().size());
+    assertEquals(2, new GeneratedClassnameFinder(logger, mainClassName
+        + "$MainClass$NestedClass").getClassNames().size());
+  }
+
+  public void testAnonymous() {
+    assertEquals(1, new AnonymousTester().getGeneratedClasses().size());
+  }
+
+  public void testEnum() {
+    assertEquals(0, new EnumTester().getGeneratedClasses().size());
+  }
+
+  public void testJavacWeirdness() {
+    List<String> classNames = new  
JavacWeirdnessTester().getGeneratedClasses();
+    assertEquals(3, classNames.size());
+    assertTrue(classNames.get(0) + " should not contain Foo",
+        classNames.get(0).indexOf("Foo") == -1);
+    assertTrue(classNames.get(1) + " should contain Foo",
+        classNames.get(1).indexOf("Foo") != -1);
+    assertTrue(classNames.get(2) + " should contain Foo",
+        classNames.get(2).indexOf("Foo") != -1);
+  }
+
+  public void testNamedLocal() {
+    assertEquals(2, new NamedLocalTester().getGeneratedClasses().size());
+  }
+
+  public void testNested() {
+    assertEquals(2, new NestedTester().getGeneratedClasses().size());
+  }
+
+  public void testStatic() {
+    assertEquals(0, new StaticTester().getGeneratedClasses().size());
+  }
+
+  public void testTopLevel() {
+    assertEquals(1, new TopLevelTester().getGeneratedClasses().size());
+  }
+
+}
+
+/**
+ * For testing a class containing an anonymous inner class.
+ */
+class AnonymousTester {
+  interface TestInterface {
+    void foo();
+  }
+
+  void foo() {
+    TestInterface a = new TestInterface() {
+      public void foo() {
+      }
+    };
+    a.foo();
+  }
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+}
+
+/**
+ * For testing a class with an Enum (for which javac generates a synthetic
+ * class).
+ */
+class EnumTester {
+  enum EnumClass {
+    A, B, C,
+  }
+
+  void foo() {
+    EnumClass et = EnumClass.A;
+    switch (et) {
+      case A:
+        break;
+    }
+  }
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+}
+
+/**
+ * Javac generates weird code for the following class. It passes a  
synthetic
+ * class ...Tester$1 as a first parameter to constructors of Fuji and  
Granny.
+ * Normally, it generates the synthetic class, but in this case, it  
decides not
+ * to generate the class. However, the bytecode still has reference to the
+ * synthetic class -- it just passes null for the synthetic class.
+ *
+ * This code also tests for an anonymous class extending a named local  
class.
+ */
+class JavacWeirdnessTester {
+  private abstract static class Apple implements Fruit {
+  }
+
+  private static interface Fruit {
+  }
+
+  private static class Fuji extends Apple {
+  }
+
+  private static class Granny extends Apple {
+  }
+
+  private static volatile boolean TRUE = true;
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+
+  private void assertEquals(Object a, Object b) {
+  }
+
+  private void testArrayStore() {
+    Apple[] apple = TRUE ? new Granny[3] : new Apple[3];
+    Apple g = TRUE ? (Apple) new Granny() : (Apple) new Fuji();
+    Apple a = apple[0] = g;
+    assertEquals(g, a);
+  }
+
+  private void testDeadTypes() {
+    if (false) {
+      new Object() {
+      }.toString();
+
+      class Foo {
+        void a() {
+        }
+      }
+      new Foo().a();
+    }
+  }
+
+  private void testLocalClasses() {
+    class Foo {
+      public Foo(int j) {
+        assertEquals(1, j);
+      };
+    }
+    final int i;
+    new Foo(i = 1) {
+      {
+        assertEquals(1, i);
+      }
+    };
+    assertEquals(1, i);
+  }
+
+  private void testReturnStatementInCtor() {
+    class Foo {
+      int i;
+
+      Foo(int i) {
+        this.i = i;
+        if (i == 0) {
+          return;
+        } else if (i == 1) {
+          return;
+        }
+        return;
+      }
+    }
+    assertEquals(new Foo(0).i, 0);
+  }
+}
+
+/**
+ * For testing a class with a generated name like $1Foo.
+ */
+class NamedLocalTester {
+  void foo1() {
+    if (false) {
+      class Foo {
+        void foo() {
+        }
+      }
+      new Foo().foo();
+    }
+  }
+
+  void foo2() {
+    class Foo {
+      void foo() {
+      }
+    }
+    new Foo().foo();
+  }
+
+  void foo3() {
+    class Foo {
+      void foo() {
+      }
+    }
+    new Foo().foo();
+  }
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+}
+
+/**
+ * For testing that nested classes are examined recursively for classes  
with
+ * generated names.
+ */
+class NestedTester {
+  class MainClass {
+    class NestedClass {
+      void foo() {
+        class Foo {
+          void bar() {
+          }
+        }
+        new Foo().bar();
+      }
+    }
+
+    void foo() {
+      class Foo {
+        void bar() {
+        }
+      }
+      new Foo().bar();
+    }
+  }
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+}
+
+/**
+ * For testing classes with private static members (javac generates a  
synthetic
+ * class here but the jdt does not).
+ */
+class StaticTester {
+  private abstract static class Apple implements Fruit {
+  }
+
+  private static interface Fruit {
+    void bar();
+  }
+
+  private static class Fuji extends Apple {
+    public void bar() {
+    }
+  }
+
+  private static class Granny extends Apple {
+    public void bar() {
+    }
+  }
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+
+}
+
+/**
+ * For testing that a class with a generated name inside another top-level  
class
+ * is found.
+ */
+class TopLevelTester {
+  public void foo() {
+    GeneratedClassnameFinderTest.TestInterface a = new  
GeneratedClassnameFinderTest.TestInterface() {
+      public void foo() {
+      }
+    };
+  }
+
+  List<String> getGeneratedClasses() {
+    return (new  
GeneratedClassnameFinder(GeneratedClassnameFinderTest.logger,
+        this.getClass().getName().replace('.', '/'))).getClassNames();
+  }
+}

Modified:  
releases/1.6/dev/core/test/com/google/gwt/dev/shell/GeneratedClassnameTest.java
==============================================================================
---  
releases/1.6/dev/core/test/com/google/gwt/dev/shell/GeneratedClassnameTest.java 
 
(original)
+++  
releases/1.6/dev/core/test/com/google/gwt/dev/shell/GeneratedClassnameTest.java 
 
Wed Feb  4 14:50:42 2009
@@ -11,8 +11,8 @@
    public void testGeneratedClassnames() {
      String namesToAccept[] = {
          "Test$1", "Test$10", "Test$Foo$1", "Test$1$Foo", "Test$10$Foo",
-        "$$345", "Test$1$Foo$"};
-    String namesToReject[] =  
{"Test1", "$345", "Test$2Foo", "Test$Foo$1Bar"};
+        "$$345", "Test$1$Foo$", "Test$1Foo", "Test$2Foo", "Test$Foo$1Bar"};
+    String namesToReject[] = {"Test1", "TestFoo", "Test$Foo$Bar", "$345"};

      for (String name : namesToAccept) {
        assertTrue("className = " + name + " should have been accepted",

Modified: releases/1.6/user/build.xml
==============================================================================
--- releases/1.6/user/build.xml (original)
+++ releases/1.6/user/build.xml Wed Feb  4 14:50:42 2009
@@ -12,6 +12,11 @@

    <fileset id="default.emma.tests" dir="${javac.junit.out}"
         includes="**/EmmaClassLoadingTest.class" />
+
+  <fileset id="default.hosted.emma.tests" dir="${javac.junit.out}"
+      excludes="**/*jjs.test.CoverageTest.class" includes="**/*Test.class"  
/>
+  <!-- everything succeeds except CoverageTest.java. It fails due to a  
javac bug in sun/OpenJDK's Java. See the file contents for details -->
+
    <!--
      Default web mode test cases
    -->
@@ -96,6 +101,15 @@
      </gwt.junit>
    </target>

+  <target name="test.hosted.emma" depends="compile, compile.tests"  
description="Run all hosted-mode tests in emma mode.">
+    <gwt.junit test.args="${test.args}"  
test.out="${junit.out}/${build.host.platform}-hosted-mode"  
test.cases="default.hosted.emma.tests" >
+      <extraclasspaths>
+        <pathelement location="${gwt.build}/out/dev/core/bin-test" />
+        <pathelement location="${gwt.tools.redist}/emma/emma.jar" />
+      </extraclasspaths>
+    </gwt.junit>
+  </target>
+
    <target name="test.hosted" depends="compile, compile.tests"  
description="Run only hosted-mode tests for this project.">
      <gwt.junit test.args="${test.args}"  
test.out="${junit.out}/${build.host.platform}-hosted-mode"  
test.cases="default.hosted.tests" >
        <extraclasspaths>
@@ -107,7 +121,7 @@
          <pathelement location="${gwt.build}/out/dev/core/bin-test" />
          <pathelement location="${gwt.tools.redist}/emma/emma.jar" />
        </extraclasspaths>
-    </gwt.junit>
+    </gwt.junit>
    </target>

    <target name="test.web" depends="compile, compile.tests"  
description="Run only web-mode tests for this project.">

Modified:  
releases/1.6/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java
==============================================================================
--- releases/1.6/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java        
 
(original)
+++ releases/1.6/user/test/com/google/gwt/dev/jjs/test/CoverageTest.java        
 
Wed Feb  4 14:50:42 2009
@@ -95,6 +95,12 @@

        new InnerSub().new InnerSubSub().fda();
        new SecondMain().new FunkyInner();
+      /*
+       * The statement below causes a javac bug in openJdk and sun's java  
6. It
+       * produces incorrect bytecode that fails with a  
java.lang.VerifyError --
+       * see Google's internal issue 1628473. This is likely to be an  
hindrance
+       * if and when GWT attempts to read bytecode directly.
+       */
        new NamedLocal().new NamedLocalSub().foo();
      }


Modified:  
releases/1.6/user/test/com/google/gwt/dev/shell/rewrite/client/EmmaClassLoadingTest.java
==============================================================================
---  
releases/1.6/user/test/com/google/gwt/dev/shell/rewrite/client/EmmaClassLoadingTest.java
         
(original)
+++  
releases/1.6/user/test/com/google/gwt/dev/shell/rewrite/client/EmmaClassLoadingTest.java
         
Wed Feb  4 14:50:42 2009
@@ -16,6 +16,9 @@
  package com.google.gwt.dev.shell.rewrite.client;

  import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.Timer;
+
+import junit.framework.TestCase;

  /**
   * Test-case to check if the jsni blocks are mapped correctly between the
@@ -31,7 +34,7 @@
   */
  public class EmmaClassLoadingTest extends GWTTestCase {

-  enum EnumTest {
+  enum EnumClass {
      A, B, C,
    }

@@ -40,7 +43,7 @@
    }

    private static String messages[] = {
-      "a foo", "b foo", "enum A", "d foo", "e foo"};
+      "1a foo", "1b foo", "1enum A", "1d foo", "1e foo"};

    private static int logCount = 0;

@@ -56,14 +59,14 @@
    public void test1() {
      TestInterface a = new TestInterface() {
        public void foo() {
-        log("a foo");
+        log("1a foo");
        }
      };
      a.foo();

      TestInterface b = new TestInterface() {
        public native void foo() /*-{
-         
@com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("b
  
foo");
+         
@com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("1b
  
foo");
        }-*/;
      };
      b.foo();
@@ -75,10 +78,10 @@
          }-*/;
        };
      }
-    EnumTest et = EnumTest.A;
+    EnumClass et = EnumClass.A;
      switch (et) {
        case A:
-        log("enum A");
+        log("1enum A");
          break;
        case B:
          log("ANY_FOO_2");
@@ -90,7 +93,7 @@

      TestInterface d = new TestInterface() {
        public native void foo() /*-{
-         
@com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("d
  
foo");
+         
@com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("1d
  
foo");
        }-*/;
      };
      d.foo();
@@ -103,8 +106,193 @@
       */
      TestInterface e = new TestInterface() {
        public native void foo() /*-{
-         
@com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("e
  
foo");
+         
@com.google.gwt.dev.shell.rewrite.client.EmmaClassLoadingTest::log(Ljava/lang/String;)("1e
  
foo");
        }-*/;
      };
-  }
+  }
+
+  public void test2() {
+    SecondTopLevelClass second = new SecondTopLevelClass();
+    SecondTopLevelClass.InnerClass.test();
+  }
+
+  public void test3() {
+    ThirdTopLevelClass third = new ThirdTopLevelClass();
+    third.test();
+  }
+
+  public void test4() {
+    FourthTopLevelClass fourth = new FourthTopLevelClass();
+    fourth.test();
+  }
+
+}
+
+/**
+ * Check that the algorithm correctly maps named inner classes. In this  
example,
+ * jdt generates $1Foo and $2Foo whereas javac generates $2Foo and $3Foo.
+ *
+ */
+class FourthTopLevelClass extends TestCase {
+  private static String messages[] = {"4a foo"};
+
+  private static int logCount = 0;
+
+  private static void log(String msg) {
+    assertEquals(messages[logCount++], msg);
+  }
+
+  void test() {
+    test1();
+    test2();
+  };
+
+  private void test1() {
+    if (false) {
+      class Foo {
+        public native void foo() /*-{
+           
@com.google.gwt.dev.shell.rewrite.client.FourthTopLevelClass::log(Ljava/lang/String;)("ANY_FOO");
+        }-*/;
+      }
+      new Foo().foo();
+    }
+  }
+
+  private void test2() {
+    class Foo {
+      public native void foo() /*-{
+         
@com.google.gwt.dev.shell.rewrite.client.FourthTopLevelClass::log(Ljava/lang/String;)("4a
  
foo");
+      }-*/;
+    }
+    new Foo().foo();
+  }
+
+  /*
+   * Added a test3() method so that when the test fails, it fails quickly.
+   * Instead of timing out, the AssertEquals fails
+   */
+  private void test3() {
+    class Foo {
+      public native void foo() /*-{
+         
@com.google.gwt.dev.shell.rewrite.client.FourthTopLevelClass::log(Ljava/lang/String;)("4b
  
foo");
+      }-*/;
+    }
+    new Foo().foo();
+  }
+}
+
+/**
+ * Check if GWT is able to correctly compile cases when there are multiple
+ * top-level classes and when there is a need to traverse inner classes.  
This
+ * class's test method simply mirrors the test methods of  
EmmaClassLoadingTest.
+ *
+ */
+class SecondTopLevelClass extends TestCase {
+
+  static class InnerClass {
+    /**
+     * Test that mapping is constructed for something which is not in the  
main
+     * unit as well.
+     */
+    static void test() {
+      EmmaClassLoadingTest.TestInterface a = new  
EmmaClassLoadingTest.TestInterface() {
+        public void foo() {
+          log("2a foo");
+        }
+      };
+      a.foo();
+
+      EmmaClassLoadingTest.TestInterface b = new  
EmmaClassLoadingTest.TestInterface() {
+        public native void foo() /*-{
+           
@com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("2b
  
foo");
+        }-*/;
+      };
+      b.foo();
+
+      if (false) {
+        EmmaClassLoadingTest.TestInterface c = new  
EmmaClassLoadingTest.TestInterface() {
+          public native void foo() /*-{
+             
@com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("ANY_FOO_1");
+          }-*/;
+        };
+      }
+      EmmaClassLoadingTest.EnumClass et = EmmaClassLoadingTest.EnumClass.A;
+      switch (et) {
+        case A:
+          log("2enum A");
+          break;
+        case B:
+          log("ANY_FOO_2");
+          break;
+        case C:
+          log("ANY_FOO_3");
+          break;
+      }
+
+      EmmaClassLoadingTest.TestInterface d = new  
EmmaClassLoadingTest.TestInterface() {
+        public native void foo() /*-{
+           
@com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("2d
  
foo");
+        }-*/;
+      };
+      d.foo();
+
+      /*
+       * jdt generates $1 (a), $2 (b), $3 (d), $4 (e). javac generates $1  
(a),
+       * $2 (b), $3 (c), $4 (d), $5 (e), $6 (synthetic). Added e so that  
the
+       * test fails quickly. Otherwise, it had to wait for a time-out (in
+       * looking through jdt generated code for non-existent jsni methods  
of $4)
+       * to fail.
+       */
+      EmmaClassLoadingTest.TestInterface e = new  
EmmaClassLoadingTest.TestInterface() {
+        public native void foo() /*-{
+           
@com.google.gwt.dev.shell.rewrite.client.SecondTopLevelClass::log(Ljava/lang/String;)("2e
  
foo");
+        }-*/;
+      };
+    }
+  }
+
+  private static String messages[] = {
+      "2a foo", "2b foo", "2enum A", "2d foo", "2e foo"};
+
+  private static int logCount = 0;
+
+  private static void log(String msg) {
+    assertEquals(messages[logCount++], msg);
+  }
+}
+
+/**
+ * Check that the mapping algorithm is not confused by the presence of  
other
+ * inner classes.
+ */
+class ThirdTopLevelClass extends TestCase {
+
+  private static String messages[] = {"3a foo"};
+
+  private static int logCount = 0;
+
+  private static void log(String msg) {
+    assertEquals(messages[logCount++], msg);
+  }
+
+  void test() {
+    Timer t1 = new Timer() {
+      @Override
+      public void run() {
+      }
+    };
+
+    EmmaClassLoadingTest.TestInterface a = new  
EmmaClassLoadingTest.TestInterface() {
+      public native void foo() /*-{
+         
@com.google.gwt.dev.shell.rewrite.client.ThirdTopLevelClass::log(Ljava/lang/String;)("3a
  
foo");
+      }-*/;
+    };
+    a.foo();
+
+    Timer t2 = new Timer() {
+      @Override
+      public void run() {
+      }
+    };
+  }
  }

--~--~---------~--~----~------------~-------~--~----~
http://groups.google.com/group/Google-Web-Toolkit-Contributors
-~----------~----~----~----~------~----~------~--~---

Reply via email to