- Fixes the incompatible exceptions in JSValueGlue 
- Adds toString implementation 
- Hacky fix for handling NativeString type 
- Removed an unnecessary variable

Patch by: amitmanjhi
Review by: jat (TBR)



git-svn-id: https://google-web-toolkit.googlecode.com/svn/branches/farewellSwt@6146 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/oophm/overlay/com/google/gwt/dev/shell/JsValueGlue.java b/dev/oophm/overlay/com/google/gwt/dev/shell/JsValueGlue.java
index 2d3f953..02e2cdd 100644
--- a/dev/oophm/overlay/com/google/gwt/dev/shell/JsValueGlue.java
+++ b/dev/oophm/overlay/com/google/gwt/dev/shell/JsValueGlue.java
@@ -170,7 +170,12 @@
     }
 
     // Just don't know what do to with this.
-    throw new HostedModeException(msgPrefix + ": JS value of type "
+    /*
+     * TODO (amitmanjhi): does throwing a HostedModeException here and catching
+     * a RuntimeException in user test
+     * com.google.gwt.dev.jjs.test.HostedTest::testObjectReturns() make sense
+     */
+    throw new IllegalArgumentException(msgPrefix + ": JS value of type "
         + value.getTypeString() + ", expected "
         + TypeInfo.getSourceRepresentation(type));
   }
diff --git a/dev/oophm/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java b/dev/oophm/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java
index 201dafb..e7bb7a7 100644
--- a/dev/oophm/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java
+++ b/dev/oophm/src/com/google/gwt/dev/shell/HtmlUnitSessionHandler.java
@@ -46,24 +46,61 @@
  */
 public class HtmlUnitSessionHandler extends SessionHandler {
 
+  private class ToStringMethod extends ScriptableObject implements Function {
+
+    private static final int EXPECTED_NUM_ARGS = 0;
+    private static final long serialVersionUID = 1592865718416163348L;
+
+    public Object call(Context context, Scriptable scope, Scriptable thisObj,
+        Object[] args) {
+      // Allow extra arguments for forward compatibility
+      if (args.length < EXPECTED_NUM_ARGS) {
+        throw Context.reportRuntimeError("Bad number of parameters for function"
+            + " toString: expected "
+            + EXPECTED_NUM_ARGS
+            + ", got "
+            + args.length);
+      }
+      // thisObj is the javaObject.
+      Value thisValue = makeValueFromJsval(context, thisObj);
+      return JavaObject.getReturnValueFromJavaMethod(context,
+          HtmlUnitSessionHandler.this, sessionData.getChannel(),
+          TO_STRING_DISPATCH_ID, thisValue, EMPTY_VALUES);
+    }
+
+    public Scriptable construct(Context cx, Scriptable scope, Object[] args) {
+      throw Context.reportRuntimeError("Function connect can't be used as a "
+          + "constructor");
+    }
+
+    @Override
+    public String getClassName() {
+      return "function toString";
+    }
+  }
+
+  private static final Value EMPTY_VALUES[] = new Value[0];
+  private static final int TO_STRING_DISPATCH_ID = 0;
+
   Map<Integer, JavaObject> javaObjectCache;
-  int nextId;
 
   /**
    * The htmlPage is also used to synchronize calls to Java code.
    */
   private HtmlPage htmlPage;
-
   private Set<Integer> javaObjectsToFree;
-  private JavaScriptEngine jsEngine;
-  private IdentityHashMap<Scriptable, Integer> jsObjectToRef;
 
+  private JavaScriptEngine jsEngine;
+
+  private IdentityHashMap<Scriptable, Integer> jsObjectToRef;
   private final PrintWriterTreeLogger logger = new PrintWriterTreeLogger();
 
   private int nextRefId = 1;
   private Map<Integer, Scriptable> refToJsObject;
-
   private SessionData sessionData;
+
+  private final ToStringMethod toStringMethod = new ToStringMethod();
+
   private final Window window;
 
   HtmlUnitSessionHandler(Window window) {
@@ -78,9 +115,8 @@
     javaObjectsToFree = new HashSet<Integer>();
     nextRefId = 1;
     refToJsObject = new HashMap<Integer, Scriptable>();
-    
+
     // related to JavaObject cache.
-    nextId = 1; // skipping zero, reserved.
     javaObjectCache = new HashMap<Integer, JavaObject>();
   }
 
@@ -101,7 +137,7 @@
   public JavaObject getOrCreateJavaObject(int refId, Context context) {
     JavaObject javaObject = javaObjectCache.get(refId);
     if (javaObject == null) {
-      javaObject = new JavaObject(context, sessionData, nextId++);
+      javaObject = new JavaObject(context, sessionData, refId);
       javaObjectCache.put(refId, javaObject);
     }
     return javaObject;
@@ -114,6 +150,10 @@
         "getProperty should not be called on the client-side");
   }
 
+  public Object getToStringTearOff(Context jsContext) {
+    return toStringMethod;
+  }
+
   public String getUserAgent() {
     return "HtmlUnit-"
         + jsEngine.getWebClient().getBrowserVersion().getUserAgent();
@@ -202,6 +242,17 @@
       return returnVal;
     }
     if (value instanceof Scriptable) {
+      if (value instanceof ScriptableObject) {
+        /*
+         * HACK: check for native types like NativeString. NativeString is
+         * package-protected. What other types do we need to check?
+         */
+        ScriptableObject scriptableValue = (ScriptableObject) value;
+        String className = scriptableValue.getClassName();
+        if (className.equals("String")) {
+          return new Value(scriptableValue.toString());
+        }
+      }
       Integer refId = jsObjectToRef.get((Scriptable) value);
       if (refId == null) {
         refId = nextRefId++;
diff --git a/dev/oophm/src/com/google/gwt/dev/shell/JavaObject.java b/dev/oophm/src/com/google/gwt/dev/shell/JavaObject.java
index c400c28..6f0b585 100644
--- a/dev/oophm/src/com/google/gwt/dev/shell/JavaObject.java
+++ b/dev/oophm/src/com/google/gwt/dev/shell/JavaObject.java
@@ -41,6 +41,32 @@
         javaRef.getRefid(), context);
   }
 
+  static Object getReturnValueFromJavaMethod(Context cx,
+      HtmlUnitSessionHandler sessionHandler, BrowserChannel channel,
+      int dispatchId, Value thisValue, Value valueArgs[]) {
+    ReturnMessage returnMessage = null;
+    synchronized (sessionHandler.getHtmlPage()) {
+      try {
+        new InvokeOnServerMessage(channel, dispatchId, thisValue, valueArgs).send();
+      } catch (IOException e) {
+        return Undefined.instance;
+      }
+      try {
+        returnMessage = ((BrowserChannelClient) channel).reactToMessagesWhileWaitingForReturn(sessionHandler);
+      } catch (IOException e) {
+        return Undefined.instance;
+      } catch (BrowserChannelException e) {
+        return Undefined.instance;
+      }
+    }
+    Value returnValue = returnMessage.getReturnValue();
+    if (returnMessage.isException()) {
+      throw new RuntimeException("JavaObject.call failed, returnMessage: "
+          + returnValue.toString());
+    }
+    return sessionHandler.makeJsvalFromValue(cx, returnValue);
+  }
+
   static boolean isJavaObject(Context jsContext, ScriptableObject javaObject) {
     return javaObject instanceof JavaObject;
   }
@@ -87,34 +113,15 @@
         args[1]);
     int dispatchId = ((Number) args[0]).intValue();
 
-    ReturnMessage returnMessage = null;
-    synchronized (sessionData.getSessionHandler().getHtmlPage()) {
-      try {
-        new InvokeOnServerMessage(sessionData.getChannel(), dispatchId,
-            thisValue, valueArgs).send();
-      } catch (IOException e) {
-        return Undefined.instance;
-      }
-      try {
-        returnMessage = ((BrowserChannelClient) sessionData.getChannel()).reactToMessagesWhileWaitingForReturn(sessionData.getSessionHandler());
-      } catch (IOException e) {
-        return Undefined.instance;
-      } catch (BrowserChannelException e) {
-        return Undefined.instance;
-      }
-    }
-    Value returnValue = returnMessage.getReturnValue();
-    if (returnMessage.isException()) {
-      throw new RuntimeException("JavaObject.call failed, returnMessage: "
-          + returnValue.toString());
-    }
     /*
      * Return a object array ret. ret[0] is a boolean indicating whether an
      * exception was thrown or not. ret[1] is the exception or the return value.
+     * If there is an exception, a RuntimeException is thrown.
      */
     Object ret[] = new Object[2];
     ret[0] = Boolean.FALSE;
-    ret[1] = sessionData.getSessionHandler().makeJsvalFromValue(cx, returnValue);
+    ret[1] = getReturnValueFromJavaMethod(cx, sessionData.getSessionHandler(),
+        sessionData.getChannel(), dispatchId, thisValue, valueArgs);
     return ret;
   }
 
@@ -125,9 +132,17 @@
 
   // ignoring the 'start' argument.
   @Override
+  public Object get(int index, Scriptable start) {
+    Value value = ServerMethods.getProperty(sessionData.getChannel(),
+        sessionData.getSessionHandler(), objectRef, index);
+    return sessionData.getSessionHandler().makeJsvalFromValue(jsContext, value);
+  }
+
+  // ignoring the 'start' argument.
+  @Override
   public Object get(String name, Scriptable start) {
     if ("toString".equals(name)) {
-      return sessionData.getToStringTearOff();
+      return sessionData.getSessionHandler().getToStringTearOff(jsContext);
     }
     if ("id".equals(name)) {
       return objectRef;
@@ -139,14 +154,6 @@
     return Undefined.instance;
   }
 
-  // ignoring the 'start' argument.
-  @Override
-  public Object get(int index, Scriptable start) {
-    Value value = ServerMethods.getProperty(sessionData.getChannel(),
-        sessionData.getSessionHandler(), objectRef, index);
-    return sessionData.getSessionHandler().makeJsvalFromValue(jsContext, value);
-  }
-
   @Override
   public String getClassName() {
     return "Class JavaObject";
diff --git a/dev/oophm/src/com/google/gwt/dev/shell/SessionData.java b/dev/oophm/src/com/google/gwt/dev/shell/SessionData.java
index 25428f2..82281c3 100644
--- a/dev/oophm/src/com/google/gwt/dev/shell/SessionData.java
+++ b/dev/oophm/src/com/google/gwt/dev/shell/SessionData.java
@@ -35,8 +35,4 @@
     return sessionHandler;
   }
   
-  public Object getToStringTearOff() {
-    // TODO Auto-generated method stub
-    return null;
-  }
 }