- CompilingClassLoader had a bug related to the JsniRef refactor
- Iterated on warnings produced by LongFromJSNIChecker
- TypeSerializerCreator now produces a suppress long warnings annotation

Review by: spoon (TBR)


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@2221 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java b/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java
index 40808d8..11afaf8 100644
--- a/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java
+++ b/dev/core/src/com/google/gwt/dev/jdt/LongFromJSNIChecker.java
@@ -19,9 +19,9 @@
 import com.google.gwt.core.ext.typeinfo.JClassType;
 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.JPrimitiveType;
 import com.google.gwt.core.ext.typeinfo.JType;
-import com.google.gwt.core.ext.typeinfo.JniConstants;
 import com.google.gwt.core.ext.typeinfo.TypeOracle;
 import com.google.gwt.dev.jjs.InternalCompilerException;
 import com.google.gwt.dev.util.Jsni;
@@ -61,41 +61,78 @@
     @Override
     public void endVisit(MethodDeclaration meth, ClassScope scope) {
       if (meth.isNative() && !suppressingWarnings(meth, scope)) {
-        // check return type
-        final TypeReference returnType = meth.returnType;
-        if (containsLong(returnType, scope)) {
-          warn(meth, "JSNI method with return type of " + returnType);
-        }
+        checkDecl(meth, scope);
+        checkRefs(meth, scope);
+      }
+    }
 
-        // check parameter types
-        if (meth.arguments != null) {
-          for (Argument arg : meth.arguments) {
-            if (containsLong(arg.type, scope)) {
-              warn(arg, "JSNI method with a parameter of type " + arg.type);
-            }
+    private void checkDecl(MethodDeclaration meth, ClassScope scope) {
+      TypeReference returnType = meth.returnType;
+      if (containsLong(returnType, scope)) {
+        warn(meth, "Return value of type '" + returnType
+            + "' is an opaque, non-numeric value in JS code");
+      }
+
+      if (meth.arguments != null) {
+        for (Argument arg : meth.arguments) {
+          if (containsLong(arg.type, scope)) {
+            warn(arg, "Parameter '" + String.valueOf(arg.name) + "': '"
+                + arg.type + "' is an opaque, non-numeric value in JS code");
           }
         }
+      }
+    }
 
-        // check JSNI references
-        FindJsniRefVisitor jsniRefsVisitor = new FindJsniRefVisitor();
-        meth.traverse(jsniRefsVisitor, scope);
-        Set<String> jsniRefs = jsniRefsVisitor.getJsniRefs();
+    private void checkFieldRef(MethodDeclaration meth, JsniRef jsniRef) {
+      assert jsniRef.isField();
+      JField target = getField(jsniRef);
+      if (target == null) {
+        return;
+      }
+      if (containsLong(target.getType())) {
+        warn(meth, "Referencing field '"
+            + target.getEnclosingType().getSimpleSourceName() + "."
+            + target.getName() + "': '" + target.getType()
+            + "' is an opaque, non-numeric value in JS code");
+      }
+    }
 
-        for (String jsniRefString : jsniRefs) {
-          JsniRef jsniRef = JsniRef.parse(jsniRefString);
-          if (hasLongParam(jsniRef)) {
-            warn(meth, "Passing a long into Java from JSNI (method "
-                + jsniRef.memberName() + ")");
-          }
-          JType jsniRefType = getType(jsniRef);
-          if (containsLong(jsniRefType)) {
-            if (jsniRef.isMethod()) {
-              warn(meth, "Method " + jsniRef.memberName() + " returns type "
-                  + jsniRefType + ", which cannot be processed in JSNI code");
-            } else {
-              warn(meth, "Field " + jsniRef.memberName() + " has type "
-                  + jsniRefType + ", which cannot be processed in JSNI code");
-            }
+    private void checkMethodRef(MethodDeclaration meth, JsniRef jsniRef) {
+      assert jsniRef.isMethod();
+      JMethod target = getMethod(jsniRef);
+      if (target == null) {
+        return;
+      }
+      if (containsLong(target.getReturnType())) {
+        warn(meth, "Referencing method '"
+            + target.getEnclosingType().getSimpleSourceName() + "."
+            + target.getName() + "': return type '" + target.getReturnType()
+            + "' is an opaque, non-numeric value in JS code");
+      }
+
+      for (JParameter param : target.getParameters()) {
+        if (containsLong(param.getType())) {
+          warn(meth, "Referencing method '"
+              + target.getEnclosingType().getSimpleSourceName() + "."
+              + target.getName() + "': parameter '" + param.getName() + "': '"
+              + param.getType()
+              + "' is an opaque, non-numeric value in JS code");
+        }
+      }
+    }
+
+    private void checkRefs(MethodDeclaration meth, ClassScope scope) {
+      FindJsniRefVisitor jsniRefsVisitor = new FindJsniRefVisitor();
+      meth.traverse(jsniRefsVisitor, scope);
+      Set<String> jsniRefs = jsniRefsVisitor.getJsniRefs();
+
+      for (String jsniRefString : jsniRefs) {
+        JsniRef jsniRef = JsniRef.parse(jsniRefString);
+        if (jsniRef != null) {
+          if (jsniRef.isMethod()) {
+            checkMethodRef(meth, jsniRef);
+          } else {
+            checkFieldRef(meth, jsniRef);
           }
         }
       }
@@ -135,50 +172,56 @@
       return returnType != null && containsLong(returnType.resolveType(scope));
     }
 
+    private JClassType findType(JsniRef jsniRef) {
+      // Use source name.
+      String className = jsniRef.className();
+      className = className.replace('$', '.');
+      JClassType type = typeOracle.findType(className);
+      return type;
+    }
+
     /**
      * Returns either the type returned if this reference is "read". For a
      * field, returns the field's type. For a method, returns the method's
      * return type. If the reference cannot be resolved, returns null.
      */
-    private JType getType(JsniRef jsniRef) {
-      JClassType type = typeOracle.findType(jsniRef.className());
+    private JField getField(JsniRef jsniRef) {
+      assert jsniRef.isField();
+      JClassType type = findType(jsniRef);
       if (type == null) {
         return null;
       }
 
-      if (jsniRef.isMethod()) {
-        for (JMethod method : type.getMethods()) {
-          if (paramTypesMatch(method, jsniRef)) {
-            return method.getReturnType();
-          }
-        }
-        // no method matched
-        return null;
-      } else {
-        JField field = type.getField(jsniRef.memberName());
-        if (field != null) {
-          return field.getType();
-        }
-        // field not found
-        return null;
+      JField field = type.getField(jsniRef.memberName());
+      if (field != null) {
+        return field;
       }
+      return null;
     }
 
-    private boolean hasLongParam(JsniRef jsniRef) {
-      if (!jsniRef.isMethod()) {
-        return false;
+    /**
+     * Returns either the type returned if this reference is "read". For a
+     * field, returns the field's type. For a method, returns the method's
+     * return type. If the reference cannot be resolved, returns null.
+     */
+    private JMethod getMethod(JsniRef jsniRef) {
+      assert jsniRef.isMethod();
+      JClassType type = findType(jsniRef);
+      if (type == null) {
+        return null;
       }
-      for (String type : jsniRef.paramTypes()) {
-        if (type.charAt(type.length() - 1) == JniConstants.DESC_LONG) {
-          return true;
+
+      for (JMethod method : type.getMethods()) {
+        if (paramTypesMatch(method, jsniRef)) {
+          return method;
         }
       }
-      return false;
+      return null;
     }
 
     private boolean paramTypesMatch(JMethod method, JsniRef jsniRef) {
-      JsniRef methodJsni = JsniRef.parse(Jsni.getJsniSignature(method));
-      return methodJsni.equals(jsniRef);
+      String methodSig = Jsni.getMemberSignature(method);
+      return methodSig.equals(jsniRef.memberSignature());
     }
 
     private boolean suppressingWarnings(MethodDeclaration meth, ClassScope scope) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
index 546409f..e5ba2f5 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GenerateJavaAST.java
@@ -34,6 +34,7 @@
 import com.google.gwt.dev.jjs.ast.JClassType;
 import com.google.gwt.dev.jjs.ast.JConditional;
 import com.google.gwt.dev.jjs.ast.JContinueStatement;
+import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JDoStatement;
 import com.google.gwt.dev.jjs.ast.JDoubleLiteral;
 import com.google.gwt.dev.jjs.ast.JEnumField;
@@ -52,7 +53,6 @@
 import com.google.gwt.dev.jjs.ast.JLabeledStatement;
 import com.google.gwt.dev.jjs.ast.JLiteral;
 import com.google.gwt.dev.jjs.ast.JLocal;
-import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
 import com.google.gwt.dev.jjs.ast.JLocalRef;
 import com.google.gwt.dev.jjs.ast.JLongLiteral;
 import com.google.gwt.dev.jjs.ast.JMethod;
@@ -278,7 +278,7 @@
           // look for a method
           String almostMatches = null;
           String methodName = parsed.memberName();
-          String jsniSig = methodName + "(" + parsed.paramTypesString() + ")";
+          String jsniSig = parsed.memberSignature();
           if (type == null) {
             if (jsniSig.equals("nullMethod()")) {
               return program.getNullMethod();
diff --git a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
index 5475668..ba0102e 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -116,7 +116,7 @@
       String className = parsed.className();
       DispatchClassInfo dispClassInfo = getClassInfoFromClassName(className);
       if (dispClassInfo != null) {
-        String memberName = parsed.memberName();
+        String memberName = parsed.memberSignature();
         int memberId = dispClassInfo.getMemberId(memberName);
         if (memberId < 0) {
           logger.log(TreeLogger.WARN, "Member '" + memberName
diff --git a/dev/core/src/com/google/gwt/dev/util/Jsni.java b/dev/core/src/com/google/gwt/dev/util/Jsni.java
index d3b33c2..1b8ed8d 100644
--- a/dev/core/src/com/google/gwt/dev/util/Jsni.java
+++ b/dev/core/src/com/google/gwt/dev/util/Jsni.java
@@ -282,12 +282,17 @@
    * determine whether one method overrides another).
    */
   public static String getJsniSignature(JMethod method) {
-    String name = method.getName();
-    String className = method.getEnclosingType().getQualifiedSourceName();
+    return method.getEnclosingType().getQualifiedSourceName() + "::"
+        + getMemberSignature(method);
+  }
 
+  /**
+   * Gets a unique name for this method and its signature (this is used to
+   * determine whether one method overrides another).
+   */
+  public static String getMemberSignature(JMethod method) {
+    String name = method.getName();
     StringBuffer sb = new StringBuffer();
-    sb.append(className);
-    sb.append("::");
     sb.append(name);
     sb.append("(");
     JParameter[] params = method.getParameters();
@@ -297,8 +302,8 @@
       sb.append(typeSig);
     }
     sb.append(")");
-    String fullName = sb.toString();
-    return fullName;
+    String result = sb.toString();
+    return result;
   }
 
   /**
diff --git a/dev/core/src/com/google/gwt/dev/util/JsniRef.java b/dev/core/src/com/google/gwt/dev/util/JsniRef.java
index 68f9a4c..c7b4dae 100644
--- a/dev/core/src/com/google/gwt/dev/util/JsniRef.java
+++ b/dev/core/src/com/google/gwt/dev/util/JsniRef.java
@@ -24,7 +24,7 @@
 /**
  * A parsed Java reference from within a JSNI method.
  */
-public class JsniRef {
+public final class JsniRef {
 
   /**
    * A regex pattern for a Java reference in JSNI code. Its groups are:
@@ -126,10 +126,7 @@
 
   @Override
   public boolean equals(Object obj) {
-    if (obj instanceof JsniRef) {
-      return toString().equals(obj.toString());
-    }
-    return false;
+    return (obj instanceof JsniRef) && toString().equals(obj.toString());
   }
 
   @Override
@@ -137,14 +134,26 @@
     return toString().hashCode();
   }
 
-  public final boolean isMethod() {
-    return paramTypesString() != null;
+  public boolean isField() {
+    return paramTypesString == null;
+  }
+
+  public boolean isMethod() {
+    return paramTypesString != null;
   }
 
   public String memberName() {
     return memberName;
   }
 
+  public String memberSignature() {
+    String ret = memberName;
+    if (isMethod()) {
+      ret += "(" + paramTypesString + ")";
+    }
+    return ret;
+  }
+
   /**
    * Return the list of parameter types for the method referred to by this
    * reference.
@@ -159,10 +168,6 @@
 
   @Override
   public String toString() {
-    String ret = "@" + className + "::" + memberName;
-    if (isMethod()) {
-      ret += "(" + paramTypesString + ")";
-    }
-    return ret;
+    return "@" + className + "::" + memberSignature();
   }
 }
diff --git a/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java b/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java
index faa5885..8e41ad7 100644
--- a/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java
+++ b/dev/core/test/com/google/gwt/dev/jdt/LongFromJSNITest.java
@@ -34,8 +34,10 @@
     code.append("  $wnd.alert(\"x is: \"+this.@Buggy::x); }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 3,
-        "Field x has type long, which cannot be processed in JSNI code");
+    shouldGenerateWarning(
+        code,
+        3,
+        "Referencing field 'Buggy.x': 'long' is an opaque, non-numeric value in JS code");
   }
 
   public void testLongArray() throws UnableToCompleteException {
@@ -46,8 +48,10 @@
     code.append("    $wnd.alert(this.@Buggy::m()()); }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 3,
-        "Method m returns type long[], which cannot be processed in JSNI code");
+    shouldGenerateWarning(
+        code,
+        3,
+        "Referencing method \'Buggy.m\': return type \'long[]\' is an opaque, non-numeric value in JS code");
   }
 
   public void testLongParameter() throws UnableToCompleteException {
@@ -56,7 +60,8 @@
     code.append("  native void jsniMeth(long x) /*-{ return; }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 2, "JSNI method with a parameter of type long");
+    shouldGenerateWarning(code, 2,
+        "Parameter 'x': 'long' is an opaque, non-numeric value in JS code");
   }
 
   public void testLongReturn() throws UnableToCompleteException {
@@ -65,7 +70,8 @@
     code.append("  native long jsniMeth() /*-{ return 0; }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 2, "JSNI method with return type of long");
+    shouldGenerateWarning(code, 2,
+        "Return value of type 'long' is an opaque, non-numeric value in JS code");
   }
 
   public void testMethodArgument() throws UnableToCompleteException {
@@ -75,8 +81,10 @@
     code.append("  native void jsniMeth() /*-{ this.@Buggy::print(J)(0); }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 3,
-        "Passing a long into Java from JSNI (method print)");
+    shouldGenerateWarning(
+        code,
+        3,
+        "Referencing method \'Buggy.print\': parameter \'x\': \'long\' is an opaque, non-numeric value in JS code");
   }
 
   public void testMethodReturn() throws UnableToCompleteException {
@@ -87,8 +95,10 @@
     code.append("    $wnd.alert(this.@Buggy::m()()); }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 3,
-        "Method m returns type long, which cannot be processed in JSNI code");
+    shouldGenerateWarning(
+        code,
+        3,
+        "Referencing method 'Buggy.m': return type 'long' is an opaque, non-numeric value in JS code");
   }
 
   public void testOverloadedMethodWithNoWarning()
@@ -114,8 +124,10 @@
     code.append("    $wnd.alert(this.@Buggy::m(I)(10)); }-*/;\n");
     code.append("}\n");
 
-    shouldGenerateWarning(code, 4,
-        "Method m returns type long, which cannot be processed in JSNI code");
+    shouldGenerateWarning(
+        code,
+        4,
+        "Referencing method 'Buggy.m': return type 'long' is an opaque, non-numeric value in JS code");
   }
 
   public void testSuppressWarnings() throws UnableToCompleteException {
diff --git a/dev/core/test/com/google/gwt/dev/util/JsniRefTest.java b/dev/core/test/com/google/gwt/dev/util/JsniRefTest.java
index 47bc926..5dddc44 100644
--- a/dev/core/test/com/google/gwt/dev/util/JsniRefTest.java
+++ b/dev/core/test/com/google/gwt/dev/util/JsniRefTest.java
@@ -27,14 +27,18 @@
       JsniRef ref = JsniRef.parse("@some.package.SomeClass::someField");
       assertEquals("some.package.SomeClass", ref.className());
       assertEquals("someField", ref.memberName());
+      assertEquals("someField", ref.memberSignature());
       assertFalse(ref.isMethod());
+      assertTrue(ref.isField());
     }
 
     {
       JsniRef ref = JsniRef.parse("@some.package.SomeClass::someMeth()");
       assertEquals("some.package.SomeClass", ref.className());
       assertEquals("someMeth", ref.memberName());
+      assertEquals("someMeth()", ref.memberSignature());
       assertTrue(ref.isMethod());
+      assertFalse(ref.isField());
       assertEquals(0, ref.paramTypes().length);
     }
 
@@ -44,6 +48,8 @@
           + "[[ZBCDFIJLjava/lang/String;S)");
       assertEquals("some.package.SomeClass", ref.className());
       assertEquals("someMeth", ref.memberName());
+      assertEquals("someMeth([[ZBCDFIJLjava/lang/String;S)",
+          ref.memberSignature());
       assertTrue(ref.isMethod());
       assertEquals(9, ref.paramTypes().length);
       assertEquals("[[Z", ref.paramTypes()[0]);
@@ -62,7 +68,9 @@
       JsniRef ref = JsniRef.parse("some.package.SomeClass::someField");
       assertEquals("some.package.SomeClass", ref.className());
       assertEquals("someField", ref.memberName());
+      assertEquals("someField", ref.memberSignature());
       assertFalse(ref.isMethod());
+      assertTrue(ref.isField());
     }
   }
 
diff --git a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
index 65a842d..3a12ab4 100644
--- a/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
+++ b/user/src/com/google/gwt/user/rebind/rpc/TypeSerializerCreator.java
@@ -242,6 +242,7 @@
   }
 
   private void writeCreateMethodMapMethod() {
+    srcWriter.println("@SuppressWarnings(\"restriction\")");
     srcWriter.println("private static native JavaScriptObject createMethodMap() /*-" + '{');
     {
       srcWriter.indent();