Makes NPE extend from JsException

This is so that TypeErrors could be treated by compiler
as NPE. The patch doesn't change the GWT behaviour as
that would be a breaking change but enable J2CL to change
the behavior.

Change-Id: I86e83ebec477b17a30abbcf5bde1f6689d143b87
Review-Link: https://gwt-review.googlesource.com/#/c/14902/
diff --git a/dev/core/test/com/google/gwt/dev/CompilerTest.java b/dev/core/test/com/google/gwt/dev/CompilerTest.java
index e6aa553..2b27fb9 100644
--- a/dev/core/test/com/google/gwt/dev/CompilerTest.java
+++ b/dev/core/test/com/google/gwt/dev/CompilerTest.java
@@ -1519,6 +1519,7 @@
     staleTypeNames.remove("java.lang.Double");
     staleTypeNames.remove("java.lang.Number");
     staleTypeNames.remove("java.lang.String");
+    staleTypeNames.remove("java.lang.Throwable");
     // Show that only this little change is stale, not the whole world.
     assertEquals(2, staleTypeNames.size());
   }
@@ -2552,6 +2553,7 @@
       staleTypeNames.remove("java.lang.Double");
       staleTypeNames.remove("java.lang.Number");
       staleTypeNames.remove("java.lang.String");
+      staleTypeNames.remove("java.lang.Throwable");
       assertEquals(expectedProcessedStaleTypeNames, staleTypeNames);
     }
     return Files.toString(outputJsFile, Charsets.UTF_8);
diff --git a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
index fc5dbeb..a8b2958 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -351,7 +351,7 @@
   private static native boolean supportsErrorStack() /*-{
     // Error.stackTraceLimit is cheaper to check and available in both IE and Chrome
     if (Error.stackTraceLimit > 0) {
-      Error.stackTraceLimit = 64;
+      $wnd.Error.stackTraceLimit = Error.stackTraceLimit = 64;
       return true;
     }
 
diff --git a/user/src/com/google/gwt/user/client/rpc/core/java/lang/NullPointerException_CustomFieldSerializer.java b/user/src/com/google/gwt/user/client/rpc/core/java/lang/NullPointerException_CustomFieldSerializer.java
new file mode 100644
index 0000000..6b79190
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/rpc/core/java/lang/NullPointerException_CustomFieldSerializer.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016 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.user.client.rpc.core.java.lang;
+
+import com.google.gwt.user.client.rpc.CustomFieldSerializer;
+import com.google.gwt.user.client.rpc.SerializationException;
+import com.google.gwt.user.client.rpc.SerializationStreamReader;
+import com.google.gwt.user.client.rpc.SerializationStreamWriter;
+
+/**
+ * Custom field serializer for {@link java.lang.NullPointerException}.
+ * This is necessary since NullPointerException emul extends JsException which is inconsistent
+ * with server-side.
+ */
+public final class NullPointerException_CustomFieldSerializer
+    extends CustomFieldSerializer<NullPointerException> {
+
+  public static NullPointerException instantiate(SerializationStreamReader streamReader)
+      throws SerializationException {
+    return new NullPointerException(streamReader.readString());
+  }
+
+  public static void deserialize(
+      SerializationStreamReader streamReader, NullPointerException instance)
+      throws SerializationException {
+    instance.initCause((Throwable) streamReader.readObject());
+  }
+
+  public static void serialize(
+      SerializationStreamWriter streamWriter, NullPointerException instance)
+      throws SerializationException {
+    streamWriter.writeString(instance.getMessage());
+    streamWriter.writeObject(instance.getCause());
+  }
+
+  @Override
+  public boolean hasCustomInstantiateInstance() {
+    return true;
+  }
+
+  @Override
+  public NullPointerException instantiateInstance(SerializationStreamReader streamReader)
+      throws SerializationException {
+    return instantiate(streamReader);
+  }
+
+  @Override
+  public void deserializeInstance(
+      SerializationStreamReader streamReader, NullPointerException instance)
+      throws SerializationException {
+    deserialize(streamReader, instance);
+  }
+
+  @Override
+  public void serializeInstance(
+      SerializationStreamWriter streamWriter, NullPointerException instance)
+      throws SerializationException {
+    serialize(streamWriter, instance);
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/lang/JsException.java b/user/super/com/google/gwt/emul/java/lang/JsException.java
index 47b7354..7a37e9c 100644
--- a/user/super/com/google/gwt/emul/java/lang/JsException.java
+++ b/user/super/com/google/gwt/emul/java/lang/JsException.java
@@ -21,8 +21,16 @@
  * primitives like {@code null}, numbers, etc.
  */
 public class JsException extends RuntimeException {
+
   protected JsException(Object backingJsObject) {
     super(backingJsObject);
   }
-}
 
+  JsException(String msg) {
+    super(msg);
+  }
+
+  JsException() {
+    super();
+  }
+}
diff --git a/user/super/com/google/gwt/emul/java/lang/NullPointerException.java b/user/super/com/google/gwt/emul/java/lang/NullPointerException.java
index 4a08e96..2425878 100644
--- a/user/super/com/google/gwt/emul/java/lang/NullPointerException.java
+++ b/user/super/com/google/gwt/emul/java/lang/NullPointerException.java
@@ -15,12 +15,15 @@
  */
 package java.lang;
 
+import jsinterop.annotations.JsPackage;
+import jsinterop.annotations.JsType;
+
 /**
  * See <a
  * href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/NullPointerException.html">the
  * official Java API doc</a> for details.
  */
-public class NullPointerException extends RuntimeException {
+public class NullPointerException extends JsException {
 
   public NullPointerException() {
   }
@@ -29,7 +32,17 @@
     super(message);
   }
 
-  native Object createError(String msg) /*-{
-    return new TypeError(msg);
-  }-*/;
+  NullPointerException(Object typeError) {
+    super(typeError);
+  }
+
+  @Override
+  Object createError(String msg) {
+    return new NativeTypeError(msg);
+  }
+
+  @JsType(isNative = true, name = "TypeError", namespace = JsPackage.GLOBAL)
+  private static class NativeTypeError {
+    NativeTypeError(String msg) { }
+  }
 }
diff --git a/user/super/com/google/gwt/emul/java/lang/Throwable.java b/user/super/com/google/gwt/emul/java/lang/Throwable.java
index 94623cb..bf53e83 100644
--- a/user/super/com/google/gwt/emul/java/lang/Throwable.java
+++ b/user/super/com/google/gwt/emul/java/lang/Throwable.java
@@ -24,7 +24,11 @@
 
 import javaemul.internal.JsUtils;
 import javaemul.internal.annotations.DoNotInline;
+
+import jsinterop.annotations.JsMethod;
+import jsinterop.annotations.JsPackage;
 import jsinterop.annotations.JsProperty;
+import jsinterop.annotations.JsType;
 
 /**
  * See <a
@@ -41,11 +45,7 @@
    * The client uses the generated field serializers which can use JSNI. That
    * leaves the server free to special case Throwable so that only the
    * detailMessage field is serialized.
-   *
-   * Throwable is given special treatment by server's SerializabilityUtil class
-   * to ensure that only the detailMessage field is serialized. Changing the
-   * field modifiers below may necessitate a change to the server's
-   * SerializabilityUtil.fieldQualifiesForSerialization(Field) method.
+   * See SerializabilityUtil.
    */
   private String detailMessage;
   private transient Throwable cause;
@@ -118,9 +118,9 @@
 
   // TODO(goktug): set 'name' property to class name and 'message' to detailMessage instead when
   // they are respected by dev tools logging.
-  native Object createError(String msg) /*-{
-    return new Error(msg);
-  }-*/;
+  Object createError(String msg) {
+    return new NativeError(msg);
+  }
 
   private static native Object fixIE(Object e) /*-{
     // In IE -unlike every other browser-, the stack property is not defined until you throw it.
@@ -279,4 +279,26 @@
     String className = getClass().getName();
     return message == null ? className : className + ": " + message;
   }
+
+  @JsMethod
+  public static Throwable of(Object e) {
+    // If the JS error is already mapped to a Java Exception, use it.
+    if (e != null) {
+      Throwable throwable = JsUtils.getProperty(e, "__java$exception");
+      if (throwable != null) {
+        return throwable;
+      }
+    }
+
+    // If the JS error is being seen for the first time, map it best corresponding Java exception.
+    return e instanceof NativeTypeError ? new NullPointerException(e) : new JsException(e);
+  }
+
+  @JsType(isNative = true, name = "Error", namespace = JsPackage.GLOBAL)
+  private static class NativeError {
+    NativeError(String msg) { }
+  }
+
+  @JsType(isNative = true, name = "TypeError", namespace = JsPackage.GLOBAL)
+  private static class NativeTypeError { }
 }
diff --git a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
index 4a7b817..b0ff418 100644
--- a/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
+++ b/user/super/com/google/gwt/emul/javaemul/internal/JsUtils.java
@@ -37,6 +37,10 @@
    return string;
   }-*/;
 
+  public static native <T> T getProperty(Object map, String key) /*-{
+    return map[key];
+  }-*/;
+
   public static native void setPropertySafe(Object map, String key, Object value) /*-{
     try {
       // This may throw exception in strict mode.