Strenghten DeadCodeElimination.
Use nullness information computed by TypeTightner to simplify
expressions of the form
a == null
when a is determined to be non null.
Change-Id: Ief863dc3e2d41334c1a9521ed2a3ca526c2d2f88
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
index 696ddf7..45ac24a 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JPrimitiveType.java
@@ -75,6 +75,16 @@
}
/**
+ * Returns <code>true</code> if it's possible for this type to be
+ * <code>null</code>.
+ *
+ * @see JNonNullType
+ */
+ public boolean canBeNull() {
+ return false;
+ }
+
+ /**
* Returns a literal which has been coerced to this type, or <code>null</code>
* if no such coercion is possible.
*/
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
index 805759f..c0f3851 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JType.java
@@ -54,6 +54,14 @@
this.name = StringInterner.get().intern(name);
}
+ /**
+ * Returns <code>true</code> if it's possible for this type to be
+ * <code>null</code>.
+ *
+ * @see JNonNullType
+ */
+ public abstract boolean canBeNull();
+
public abstract JLiteral getDefaultValue();
public abstract String getJavahSignatureName();
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
index bbe809a..381f5d3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/DeadCodeElimination.java
@@ -67,6 +67,9 @@
import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
+import com.google.gwt.thirdparty.guava.common.base.Predicate;
+import com.google.gwt.thirdparty.guava.common.collect.Iterables;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
import java.lang.reflect.Method;
import java.util.ArrayList;
@@ -155,21 +158,9 @@
simplifyXor(lhs, rhs, ctx);
break;
case EQ:
- // simplify: null == null -> true
- if (lhs.getType() == program.getTypeNull() && rhs.getType() == program.getTypeNull()
- && !x.hasSideEffects()) {
- ctx.replaceMe(program.getLiteralBoolean(true));
- return;
- }
simplifyEq(lhs, rhs, ctx, false);
break;
case NEQ:
- // simplify: null != null -> false
- if (lhs.getType() == program.getTypeNull() && rhs.getType() == program.getTypeNull()
- && !x.hasSideEffects()) {
- ctx.replaceMe(program.getLiteralBoolean(false));
- return;
- }
simplifyEq(lhs, rhs, ctx, true);
break;
case ADD:
@@ -1493,13 +1484,52 @@
return false;
}
+ private AnalysisResult staticallyEvaluateEq(JExpression lhs, JExpression rhs) {
+ if (lhs.getType() == program.getTypeNull() && rhs.getType() == program.getTypeNull()) {
+ return AnalysisResult.TRUE;
+ }
+ if (lhs.getType() == program.getTypeNull() && !rhs.getType().canBeNull() ||
+ rhs.getType() == program.getTypeNull() && !lhs.getType().canBeNull()) {
+ return AnalysisResult.FALSE;
+ }
+ return AnalysisResult.UNKNOWN;
+ }
+
+ private JExpression simplifyNonSideEffects(JExpression result,
+ JExpression... evaluateIfSideEffects) {
+
+ List<JExpression> expressionsWithSideEffects = Lists.newArrayList(Iterables.filter(
+ Arrays.asList(evaluateIfSideEffects), new Predicate<JExpression>() {
+ @Override
+ public boolean apply(JExpression expression) {
+ return expression.hasSideEffects();
+ }
+ }));
+
+ if (expressionsWithSideEffects.isEmpty()) {
+ return result;
+ }
+
+ expressionsWithSideEffects.add(result);
+ return new JMultiExpression(expressionsWithSideEffects.get(0).getSourceInfo(),
+ expressionsWithSideEffects);
+ }
+
/**
* Simplify <code>lhs == rhs</code>. If <code>negate</code> is true, then
* it's actually static evaluation of <code>lhs != rhs</code>.
*/
- private void simplifyEq(JExpression lhs, JExpression rhs, Context ctx, boolean negated) {
+ private void simplifyEq(JExpression lhs, JExpression rhs, Context ctx, boolean negate) {
+ // simplify: null == null -> true
+ AnalysisResult analysisResult = staticallyEvaluateEq(lhs, rhs);
+ if (analysisResult != AnalysisResult.UNKNOWN) {
+ ctx.replaceMe(simplifyNonSideEffects(
+ program.getLiteralBoolean(negate ^ (analysisResult == AnalysisResult.TRUE)), lhs, rhs));
+ return;
+ }
+
if (isTypeBoolean(lhs) && isTypeBoolean(rhs)) {
- simplifyBooleanEq(lhs, rhs, ctx, negated);
+ simplifyBooleanEq(lhs, rhs, ctx, negate);
return;
}
}
@@ -1917,4 +1947,6 @@
optimizeEvent.end("didChange", "" + stats.didChange());
return stats;
}
+
+ private enum AnalysisResult { TRUE, FALSE, UNKNOWN };
}
diff --git a/user/test/com/google/gwt/dev/jjs/test/FieldInitOrderChild.java b/user/test/com/google/gwt/dev/jjs/test/FieldInitOrderChild.java
index 78b6fb3..56df745 100644
--- a/user/test/com/google/gwt/dev/jjs/test/FieldInitOrderChild.java
+++ b/user/test/com/google/gwt/dev/jjs/test/FieldInitOrderChild.java
@@ -56,8 +56,19 @@
// i3, i4 and i7 would be directly converted into strings hence show undefined instead of null.
// String operations should take care of corner cases where a string is null or undefined
// see issue 8257.
- seenValues.add("i1=" + i1 + ",i2=" + i2 + ",i3=" + (i3 == null ? "null" : i3) +
- ",i4=" + (i4 == null ? "null" : i4) + ",i5=" + i5 + ",i6=" + i6
- + ",i7=" + (i7 == null ? "null" : i7));
+ seenValues.add("i1=" + i1 + ",i2=" + i2 + ",i3=" + undefinedToNull(i3) +
+ ",i4=" + undefinedToNull(i4) + ",i5=" + i5 + ",i6=" + i6
+ + ",i7=" + undefinedToNull(i7));
+ }
+
+ // Convert undefined to null this way {@code undefinedToNull(i)} instead of
+ // {@code i == null ? "null" : i} to avoid the shortcomings of our nullness analysis in
+ // {@link TypeTightener}, which will infer that i3 is never null.
+ private String undefinedToNull(Object o) {
+ String s = "" + o;
+ if (s.equals("undefined")) {
+ return "null";
+ }
+ return s;
}
}
\ No newline at end of file