Do not assume that "this" is always non null.

That assumption does not necessarily hold for @JsOverlay methods
not when methods are devirtualized.

Change-Id: I8fe1f6e8f4bc2d330d407faee3327c1e899baeb2
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
index da9105f..f182b40 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JThisRef.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.dev.jjs.ast;
 
+import static com.google.gwt.thirdparty.guava.common.base.Preconditions.checkArgument;
+
 import com.google.gwt.dev.jjs.SourceInfo;
 
 /**
@@ -22,20 +24,27 @@
  */
 public class JThisRef extends JExpression {
 
-  private final JDeclaredType type;
+  private final JDeclaredType classType;
+  private final JType type;
 
-  public JThisRef(SourceInfo info, JDeclaredType type) {
+  public JThisRef(SourceInfo info, JDeclaredType classType) {
+    this(info, classType, classType);
+  }
+
+  public JThisRef(SourceInfo info, JDeclaredType classType, JType type) {
     super(info);
+    this.classType = classType;
     this.type = type;
+    checkArgument(type.getUnderlyingType().equals(classType));
   }
 
   public JDeclaredType getClassType() {
-    return type;
+    return classType;
   }
 
   @Override
   public JType getType() {
-    return type.strengthenToNonNull();
+    return type;
   }
 
   @Override
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
index 90aa0a7..4bfa138 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/MakeCallsStatic.java
@@ -174,7 +174,8 @@
         newMethod.setJsOverlay();
       }
 
-      JType thisParameterType = enclosingType.strengthenToNonNull();
+      // Do not strengthen to non null since the implicit NPE in instance dispatch is gone.
+      JType thisParameterType = enclosingType;
       // Setup parameters; map from the old params to the new params
       JParameter thisParam = newMethod.createThisParameter(sourceInfo, thisParameterType);
       Map<JParameter, JParameter> varMap = Maps.newIdentityHashMap();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
index 4b2b068..bbb2912 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeTightener.java
@@ -42,6 +42,7 @@
 import com.google.gwt.dev.jjs.ast.JReferenceType;
 import com.google.gwt.dev.jjs.ast.JReturnStatement;
 import com.google.gwt.dev.jjs.ast.JRunAsync;
+import com.google.gwt.dev.jjs.ast.JThisRef;
 import com.google.gwt.dev.jjs.ast.JTryStatement;
 import com.google.gwt.dev.jjs.ast.JType;
 import com.google.gwt.dev.jjs.ast.JVariable;
@@ -560,6 +561,17 @@
     }
 
     @Override
+    public void endVisit(JThisRef x, Context ctx) {
+      if (getCurrentMethod().isJsOverlay()) {
+        return;
+      }
+      if (x.getType().canBeNull()) {
+        ctx.replaceMe(
+            new JThisRef(x.getSourceInfo(), x.getClassType(), x.getType().strengthenToNonNull()));
+      }
+    }
+
+    @Override
     public void endVisit(JParameter x, Context ctx) {
       JMethod currentMethod = getCurrentMethod();
       if (program.codeGenTypes.contains(currentMethod.getEnclosingType())
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
index ca55f21..d6a66d7 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -126,6 +126,7 @@
           "  public static native boolean isNotNull(Object a) /*-{ }-*/;",
           "  public static native boolean jsEquals(Object a, Object b) /*-{ }-*/;",
           "  public static native boolean jsNotEquals(Object a, Object b) /*-{ }-*/;",
+          "  public static native Object maskUndefined(Object src) /*-{ }-*/;",
           "  public static int narrow_int(double x) { return 0; }",
           "  public static byte narrow_long(double x) { return 0; }",
           "}"
diff --git a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
index 7f9d57e..2811bd2 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -37,6 +37,7 @@
 
 import javaemul.internal.annotations.DoNotInline;
 import jsinterop.annotations.JsMethod;
+import jsinterop.annotations.JsOverlay;
 import jsinterop.annotations.JsPackage;
 import jsinterop.annotations.JsProperty;
 import jsinterop.annotations.JsType;
@@ -455,4 +456,21 @@
     assertTrue(Double.isNaN(getNan()));
     assertTrue(isNan(Double.NaN));
   }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
+  private static class NativeObjectWithOverlay {
+    public NativeObjectWithOverlay() { }
+    @JsOverlay
+    final NativeObjectWithOverlay getThis() {
+      return this;
+    }
+  }
+
+  public void testOveralyDispatchOnNull() {
+    // Define a variable where the compiler can not statically determine that it is actually null.
+    NativeObjectWithOverlay objectWithOverlay =
+        Math.random() > 1000 ? new NativeObjectWithOverlay() : null;
+
+    assertTrue(objectWithOverlay.getThis() == null);
+  }
 }