This fixes issue #785 and re-enables a disabled test in HostedTest related to hosted mode wrapped Java Objects answering "toString()" in JSNI code.

Review by: jat
           knorton

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@840 8db76d5a-ed1c-0410-87a9-c151d255dfc7
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 a403cd5..659343a 100644
--- a/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
+++ b/dev/core/src/com/google/gwt/dev/shell/CompilingClassLoader.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 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
@@ -83,17 +83,13 @@
      */
     public synchronized int getDispId(String jsniMemberRef) {
       /*
-       * MAGIC: toString is id 0
-       * 
-       * We need to check for toString early on to handle the JavaScript
-       * toString method. We do a case insensitive check to make sure that
-       * tostring in JavaScript would work.
+       * Map JS toString() onto the Java toString() method.
        * 
        * TODO : is it true that tostring is valid in JavaScript? JavaScript is
        * case sensitive.
        */
-      if (jsniMemberRef.equalsIgnoreCase("toString")) {
-        return 0;
+      if (jsniMemberRef.equals("toString")) {
+        jsniMemberRef = "@java.lang.Object::toString()";
       }
 
       // References are of the form "@class::field" or
@@ -205,7 +201,7 @@
         return null;
       }
 
-     if (dispClassInfo == null) {
+      if (dispClassInfo == null) {
         /*
          * we need to create a new DispatchClassInfo since we have never seen
          * this class before under any source or binary class name
diff --git a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
index c9e5b88..75fbb0a 100644
--- a/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
+++ b/dev/core/src/com/google/gwt/dev/shell/DispatchClassInfo.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 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
@@ -61,10 +61,6 @@
     return id.intValue();
   }
 
-  public Class getWrappedClass() {
-    return cls;
-  }
-
   private void addMember(Member member, String sig) {
     memberById.add(member);
     int index = memberById.size() - 1;
@@ -131,38 +127,7 @@
   private void lazyInitTargetMembers() {
     if (memberById == null) {
       memberById = new ArrayList();
-      try {
-        // MAGIC: 0 is the default property
-        memberById.add(cls.getMethod("toString", null));
-      } catch (SecurityException e) {
-        e.printStackTrace();
-      } catch (NoSuchMethodException e) {
-        /*
-         * Interfaces do not automatically inherit toString() implicitly. If
-         * they have not defined a toString() method then we will pick the one
-         * from java.lang.Object::toString() method and use that at slot zero.
-         * 
-         * TODO(mmendez): How should we handle the case where a user writes JSNI
-         * code to interact with an instance that is typed as a particular
-         * interface? Should a user write JSNI code as follows:
-         * 
-         * x.@com.google.gwt.HasFocus::equals(Ljava/lang/Object;)(y)
-         * 
-         * or
-         * 
-         * x.@java.lang.Object::equals(Ljava/lang/Object;)(y)
-         */
-        if (cls.isInterface()) {
-          try {
-            memberById.add(Object.class.getMethod("toString", null));
-          } catch (Exception e1) {
-            e1.printStackTrace();
-          }
-        } else {
-          e.printStackTrace();
-        }
-      }
-
+      memberById.add(null); // 0 is reserved; it's magic on Win32
       memberIdByName = new HashMap();
       lazyInitTargetMembersUsingReflectionHelper(cls);
     }
@@ -175,6 +140,19 @@
       lazyInitTargetMembersUsingReflectionHelper(superclass);
     }
 
+    /*
+     * TODO(mmendez): How should we handle the case where a user writes JSNI
+     * code to interact with an instance that is typed as a particular
+     * interface? Should a user write JSNI code as follows:
+     * 
+     * x.@com.google.gwt.HasFocus::equals(Ljava/lang/Object;)(y)
+     * 
+     * or
+     * 
+     * x.@java.lang.Object::equals(Ljava/lang/Object;)(y)
+     * 
+     */
+
     // Get the methods on this class/interface.
     //
     Method[] methods = targetClass.getDeclaredMethods();
diff --git a/dev/core/src/com/google/gwt/dev/shell/JavaDispatchImpl.java b/dev/core/src/com/google/gwt/dev/shell/JavaDispatchImpl.java
index 8d9ce4c..f83b84f 100644
--- a/dev/core/src/com/google/gwt/dev/shell/JavaDispatchImpl.java
+++ b/dev/core/src/com/google/gwt/dev/shell/JavaDispatchImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 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
@@ -136,17 +136,6 @@
    * @return the member
    */
   protected Member getMember(int dispId) {
-    if (dispId == 0) {
-      try {
-        return Object.class.getDeclaredMethod("toString", null);
-      } catch (SecurityException e) {
-        // Should never get here
-        e.printStackTrace();
-      } catch (NoSuchMethodException e) {
-        // Should never get here
-        e.printStackTrace();
-      }
-    }
     DispatchClassInfo clsInfo = classLoader.getClassInfoByDispId(dispId);
     return clsInfo.getMember(dispId);
   }
diff --git a/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java b/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
index 54fb699..2ab6147 100644
--- a/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
+++ b/dev/linux/src/com/google/gwt/dev/shell/moz/GeckoDispatchAdapter.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 Google Inc.
+ * Copyright 2007 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
@@ -27,7 +27,7 @@
 import java.lang.reflect.Method;
 
 /**
- * Wraps an arbitrary Java Object as a Dispatchable component. The class was
+ * Wraps an arbitrary Java Object as a Dispatch component. The class was
  * motivated by the need to expose Java objects into JavaScript.
  * 
  * An instance of this class with no target is used to globally access all
@@ -91,9 +91,6 @@
         classLoader.putMethodDispatch(method, dispMethod);
       }
       jsValue.setWrappedFunction(method.toString(), dispMethod);
-//      LowLevelMoz.wrapFunction(scriptObject, method.toString(),
-//          dispMethod, (JsValueMoz)jsValue);
-      return;
     }
   }
 
diff --git a/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java b/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
index 413a8e1..409c3bd 100644
--- a/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
+++ b/dev/mac/src/com/google/gwt/dev/shell/mac/WebKitDispatchAdapter.java
@@ -27,7 +27,7 @@
 import java.lang.reflect.Method;
 
 /**
- * Wraps an arbitrary Java Object as a Dispatchable component. The class was
+ * Wraps an arbitrary Java Object as a Dispatch component. The class was
  * motivated by the need to expose Java objects into JavaScript.
  * 
  * An instance of this class with no target is used to globally access all
@@ -44,7 +44,6 @@
    * static method calls and field references.
    * 
    * @param cl this class's classLoader
-   * @param aScriptObject the execution iframe's window
    */
   WebKitDispatchAdapter(CompilingClassLoader cl) {
     javaDispatch = new JavaDispatchImpl(cl);
@@ -55,7 +54,6 @@
    * This constructor initializes a dispatcher, around a particular instance.
    * 
    * @param cl this class's classLoader
-   * @param aScriptObject the execution iframe's window
    * @param target the object being wrapped as an IDispatch
    */
   WebKitDispatchAdapter(CompilingClassLoader cl, Object target) {
@@ -114,4 +112,9 @@
     Object val = JsValueGlue.get(jsValue, field.getType(), "setField");
     javaDispatch.setFieldValue(dispId, val);
   }
+
+  public String toString() {
+    return getTarget().toString();
+  }
+
 }
diff --git a/eclipse/settings/english.dictionary b/eclipse/settings/english.dictionary
index f3d3628..4a93e10 100644
--- a/eclipse/settings/english.dictionary
+++ b/eclipse/settings/english.dictionary
@@ -47241,3 +47241,4 @@
 slurp

 rectifies

 initialize

+initializes

diff --git a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
index bae732e..3e8003d 100644
--- a/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/HostedTest.java
@@ -31,26 +31,47 @@
  */
 public class HostedTest extends GWTTestCase {
 
+  /**
+   * Tests that we can use a source level name for a nested type instead of the
+   * binary name.
+   */
+  protected static class A {
+    
+    /**
+     * TODO: document me.
+     */
+    public static class B {
+      int b = 1;
+      public native int getUsingBinaryRef() /*-{
+      return this.@com.google.gwt.dev.jjs.test.HostedTest$A$B::b;
+    }-*/;
+      
+      public native int getUsingSourceRef() /*-{
+        return this.@com.google.gwt.dev.jjs.test.HostedTest.A.B::b;
+      }-*/;
+    }
+  }
+
   static String sFoo(String s) {
     return s + "foo";
   }
-
-  private native static JavaScriptObject getBoxedBooleanAsObject(boolean v) /*-{
-    return new Boolean(v);
-  }-*/;
   
   private native static boolean getBoxedBooleanAsBool(boolean v) /*-{
     return new Boolean(v);
   }-*/;
   
-  private native static JavaScriptObject getBoxedNumberAsObject(double v) /*-{
-    return new Number(v);
+  private native static JavaScriptObject getBoxedBooleanAsObject(boolean v) /*-{
+    return new Boolean(v);
   }-*/;
   
   private native static double getBoxedNumberAsDouble(double v) /*-{
     return new Number(v);
   }-*/;
   
+  private native static JavaScriptObject getBoxedNumberAsObject(double v) /*-{
+    return new Number(v);
+  }-*/;
+  
   private native static JavaScriptObject getBoxedStringAsObject(String v) /*-{
     return new String(v);
   }-*/;
@@ -62,6 +83,14 @@
   private native static double getDouble(double v) /*-{
     return -v;
   }-*/;
+
+  private static native float getFloat() /*-{
+    return myFloatValue;
+  }-*/;
+  
+  private static native float getFloatString() /*-{
+    return Number(myFloatValue.toString());
+  }-*/;
   
   private native static int getInt(int v) /*-{
     return -v;
@@ -71,15 +100,19 @@
   private static native Object getIntAsObject() /*-{
     return 5;
   }-*/;
-  
+
   private native static String getJSOAsString(JavaScriptObject jso) /*-{
     return "" + jso;
   }-*/;
-  
+
   private native static Object getObject(Object o) /*-{
     return o;
   }-*/;
 
+  private static native JavaScriptObject getsFooFunc() /*-{
+    return @com.google.gwt.dev.jjs.test.HostedTest::sFoo(Ljava/lang/String;);
+  }-*/;
+
   private native static String getString(String s) /*-{
     return s + "me";
   }-*/;
@@ -89,26 +122,10 @@
     return "test";
   }-*/;
 
-  private static native void storeFloat(float f) /*-{
-    myFloatValue = f;
-  }-*/;
-
-  private static native float getFloat() /*-{
-    return myFloatValue;
-  }-*/;
-
-  private static native float getFloatString() /*-{
-    return Number(myFloatValue.toString());
-  }-*/;
-
   private static native int passThroughInt(int val) /*-{
     return val;
   }-*/;
 
-  private static native JavaScriptObject getsFooFunc() /*-{
-    return @com.google.gwt.dev.jjs.test.HostedTest::sFoo(Ljava/lang/String;);
-  }-*/;
-
   private static native String sFooCall(String s) /*-{
     var func = @com.google.gwt.dev.jjs.test.HostedTest::sFoo(Ljava/lang/String;);
     return func.call(null, s);
@@ -137,175 +154,19 @@
     return fooFunc(s);
   }-*/;
 
+  private static native void storeFloat(float f) /*-{
+    myFloatValue = f;
+  }-*/;
+
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
   }
 
-  public void testBasic() {
-    int iv = getInt(14);
-    assertEquals(iv, -14);
-    double dv = getDouble(31.5);
-    assertEquals(dv, -31.5, 0.0);
-    String sv = getString("test");
-    assertEquals(sv, "testme");
-    Object oin = String.class;
-    Object oout = getObject(oin);
-    assertEquals(oin, oout);
-  }
-
   public void test32BitInt() {
     assertEquals(Integer.MAX_VALUE, passThroughInt(Integer.MAX_VALUE));
     assertEquals(Integer.MIN_VALUE, passThroughInt(Integer.MIN_VALUE));
   }
 
-  public void testByteMarshalling() {
-    byte b = 100;
-    assertEquals(100, byteAsInt(b));
-    b = -125;
-    assertEquals(-125, byteAsInt(b));
-  }
-
-  public void testFunctionCaching() {
-    assertEquals("barfoo", sFooCall("bar"));
-    assertEquals("barfoo", sFooDirect("bar"));
-    assertEquals("barfoo", sFooInvoke("bar"));
-    assertEquals("barfoo", sFooRoundTrip(getsFooFunc(), "bar"));
-
-    assertEquals("barfoo", fooCall("bar"));
-    assertEquals("barfoo", fooDirect("bar"));
-    assertEquals("barfoo", fooRoundTrip(getFooFunc(), "bar"));
-
-    String sFooString = getsFooFunc().toString();
-    assertEquals(sFooString, sFooFuncAsStr());
-    assertEquals(sFooString, sFooFuncToString());
-    String fooString = getFooFunc().toString();
-    assertEquals(fooString, fooFuncAsStr());
-    assertEquals(fooString, fooFuncToString());
-  }
-
-  public void testJsniFormats() {
-    jsniA();
-    jsniB();
-    jsniC();
-    jsniD();
-    jsniE();
-    jsniF();
-    jsniG();
-    jsniH();
-    jsniI();
-    jsniJ();
-    jsniK();
-    jsniL();
-  }
-
-  public void testFloat() {
-    storeFloat(Float.MIN_VALUE);
-    float f = getFloat();
-    assertTrue(f == Float.MIN_VALUE);
-    f = getFloatString();
-    assertTrue(f == Float.MIN_VALUE);
-    storeFloat(Float.MAX_VALUE);
-    f = getFloat();
-    assertTrue(f == Float.MAX_VALUE);
-    f = getFloatString();
-    assertTrue(f == Float.MAX_VALUE);
-  }
-  
-  public void testLocalJsni() {
-    
-    class Foo {
-      native String a() /*-{
-        return "blah";
-      }-*/;
-
-      native String b() /*-{
-        return this.@com.google.gwt.dev.jjs.test.HostedTest$1$Foo::a()();
-      }-*/;
-
-      String c() {
-        return a();
-      }
-    }
-    
-    Foo f = new Foo();
-    assertEquals(f.a(), "blah");
-    assertEquals(f.b(), "blah");
-    assertEquals(f.c(), "blah");
-    
-    Foo fo = new Foo() {
-      native String a() /*-{
-        return "oblah";
-      }-*/;
-
-      native String b() /*-{
-        return this.@com.google.gwt.dev.jjs.test.HostedTest$1$Foo::a()();
-      }-*/;
-
-      native String c() /*-{
-        return this.@com.google.gwt.dev.jjs.test.HostedTest$1::a()();
-      }-*/;
-    };
-    
-    assertEquals(fo.a(), "oblah");
-    assertEquals(fo.b(), "oblah");
-    assertEquals(fo.c(), "oblah");
-  }
-
-  public void testLongMarshalling() {
-    // a big number that cannot accurately be represented as a double
-    long l = 1234567890123456789L;
-    double d = l;
-    assertTrue(isEq(l, d));
-  }
-
-  /**
-   * Tests that we can use a source level name for a nested type instead of the
-   * binary name.
-   */
-  protected static class A {
-    
-    /**
-     * TODO: document me.
-     */
-    public static class B {
-      int b = 1;
-      public native int getUsingSourceRef() /*-{
-        return this.@com.google.gwt.dev.jjs.test.HostedTest.A.B::b;
-      }-*/;
-      
-      public native int getUsingBinaryRef() /*-{
-      return this.@com.google.gwt.dev.jjs.test.HostedTest$A$B::b;
-    }-*/;
-    }
-  }
-  
-  /**
-   * Tests that we are able to use binary and source level names when referencing
-   * a Java identifier from JSNI.
-   */
-  public void testJavaMemberRefResolution() {
-    A.B b = new A.B();
-    assertEquals(1, b.getUsingSourceRef());
-    assertEquals(1, b.getUsingBinaryRef());
-  }
-
-  /*
-   * Test that returning strings from methods declared as returning Object
-   * works, and that returning a primitive does not.
-   */
-  public void testObjectReturns() {
-    String str = (String)getStringAsObject();
-    assertEquals(str, "test");
-    try {
-      getIntAsObject();
-      // should have thrown an exception in hosted mode,
-      // so fail unless we are in web mode
-      assertTrue(GWT.isScript());
-    } catch (IllegalArgumentException e) {
-      // expected exception
-    }
-  }
-
   /*
    * Test that returning JavaScript boxed primitives works as expected.
    * Currently only String is automatically unboxed, so Boolean and Number
@@ -326,12 +187,87 @@
     assertEquals(sv, "test");
   }
 
+  public void testBasic() {
+    int iv = getInt(14);
+    assertEquals(iv, -14);
+    double dv = getDouble(31.5);
+    assertEquals(dv, -31.5, 0.0);
+    String sv = getString("test");
+    assertEquals(sv, "testme");
+    Object oin = String.class;
+    Object oout = getObject(oin);
+    assertEquals(oin, oout);
+  }
+
+  public void testByteMarshalling() {
+    byte b = 100;
+    assertEquals(100, byteAsInt(b));
+    b = -125;
+    assertEquals(-125, byteAsInt(b));
+  }
+
+  public void testFloat() {
+    storeFloat(Float.MIN_VALUE);
+    float f = getFloat();
+    assertTrue(f == Float.MIN_VALUE);
+    f = getFloatString();
+    assertTrue(f == Float.MIN_VALUE);
+    storeFloat(Float.MAX_VALUE);
+    f = getFloat();
+    assertTrue(f == Float.MAX_VALUE);
+    f = getFloatString();
+    assertTrue(f == Float.MAX_VALUE);
+  }
+  
+  public void testFunctionCaching() {
+    assertEquals("barfoo", sFooCall("bar"));
+    assertEquals("barfoo", sFooDirect("bar"));
+    assertEquals("barfoo", sFooInvoke("bar"));
+    assertEquals("barfoo", sFooRoundTrip(getsFooFunc(), "bar"));
+
+    assertEquals("barfoo", fooCall("bar"));
+    assertEquals("barfoo", fooDirect("bar"));
+    assertEquals("barfoo", fooRoundTrip(getFooFunc(), "bar"));
+
+    String sFooString = getsFooFunc().toString();
+    assertEquals(sFooString, sFooFuncAsStr());
+    assertEquals(sFooString, sFooFuncToString());
+    String fooString = getFooFunc().toString();
+    assertEquals(fooString, fooFuncAsStr());
+    assertEquals(fooString, fooFuncToString());
+  }
+
+  /**
+   * Tests that we are able to use binary and source level names when referencing
+   * a Java identifier from JSNI.
+   */
+  public void testJavaMemberRefResolution() {
+    A.B b = new A.B();
+    assertEquals(1, b.getUsingSourceRef());
+    assertEquals(1, b.getUsingBinaryRef());
+  }
+
+  public void testJsniFormats() {
+    jsniA();
+    jsniB();
+    jsniC();
+    jsniD();
+    jsniE();
+    jsniF();
+    jsniG();
+    jsniH();
+    jsniI();
+    jsniJ();
+    jsniK();
+    jsniL();
+  }
+  
   /**
    * Tests that using the JavaScript toString method results in a call to 
    * the java.lang.Object::toString() method.
    *
    */
-  public void disabledTestJSNIToStringResolution() {
+  public void testJSNIToStringResolution() {
     class Foo {
       public String toString() {
         return "FOO";
@@ -340,19 +276,83 @@
     
     assertEquals("FOO", callJSNIToString(new Foo()));
   }
-  
-  private native String callJSNIToString(Object obj) /*-{
-    return obj.toString();
-  }-*/;
+
+  public void testLocalJsni() {
+    
+    class Foo {
+      native String a() /*-{
+        return "blah";
+      }-*/;
+
+      native String b() /*-{
+        return this.@com.google.gwt.dev.jjs.test.HostedTest$2$Foo::a()();
+      }-*/;
+
+      String c() {
+        return a();
+      }
+    }
+    
+    Foo f = new Foo();
+    assertEquals(f.a(), "blah");
+    assertEquals(f.b(), "blah");
+    assertEquals(f.c(), "blah");
+    
+    Foo fo = new Foo() {
+      native String a() /*-{
+        return "oblah";
+      }-*/;
+
+      native String b() /*-{
+        return this.@com.google.gwt.dev.jjs.test.HostedTest$2$Foo::a()();
+      }-*/;
+
+      native String c() /*-{
+        return this.@com.google.gwt.dev.jjs.test.HostedTest$1::a()();
+      }-*/;
+    };
+    
+    assertEquals(fo.a(), "oblah");
+    assertEquals(fo.b(), "oblah");
+    assertEquals(fo.c(), "oblah");
+  }
+
+  public void testLongMarshalling() {
+    // a big number that cannot accurately be represented as a double
+    long l = 1234567890123456789L;
+    double d = l;
+    assertTrue(isEq(l, d));
+  }
+
+  /*
+   * Test that returning strings from methods declared as returning Object
+   * works, and that returning a primitive does not.
+   */
+  public void testObjectReturns() {
+    String str = (String)getStringAsObject();
+    assertEquals(str, "test");
+    try {
+      getIntAsObject();
+      // should have thrown an exception in hosted mode,
+      // so fail unless we are in web mode
+      assertTrue(GWT.isScript());
+    } catch (IllegalArgumentException e) {
+      // expected exception
+    }
+  }
   
   String foo(String s) {
     return s + "foo";
   }
-
+  
   private native int byteAsInt(byte b) /*-{
     return b;
   }-*/;
 
+  private native String callJSNIToString(Object obj) /*-{
+    return obj.toString();
+  }-*/;
+
   private native String fooCall(String s) /*-{
     var f = this.@com.google.gwt.dev.jjs.test.HostedTest::foo(Ljava/lang/String;);
     return f.call(this, s);