summaryrefslogtreecommitdiff
path: root/compiler/src/main/java/android/databinding
diff options
context:
space:
mode:
authorYigit Boyar <yboyar@google.com>2017-12-28 17:13:44 -0800
committerYigit Boyar <yboyar@google.com>2018-01-09 16:55:38 +0000
commitc56acaa6c114d8a4a463b97f6b02b6a1d508f152 (patch)
treeb4f07f1291578c6bae1a6eddc6a668c09301d765 /compiler/src/main/java/android/databinding
parent28efa96610b913ce5b30cc36ed33dbf1b62f3f66 (diff)
downloaddata-binding-c56acaa6c114d8a4a463b97f6b02b6a1d508f152.tar.gz
Detect common null check ternary ops
This CL adds logic to the Ternary expression to avoid unboxing if the predicate is a null check. e.g. if it is a == null ? b : a, we don't need to unbox for the false statement because it will not be executed if a is null. This is not a complete nullability detection code, it only covers the common cases. Bug: 37127560 Test: SafeUnboxingTest Change-Id: I7a090b7dc92f1c127cc3b63f7eab3f6c37a250a8
Diffstat (limited to 'compiler/src/main/java/android/databinding')
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java34
-rw-r--r--compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java26
2 files changed, 56 insertions, 4 deletions
diff --git a/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java b/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
index 8dcb63f1..ad294c17 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/ComparisonExpr.java
@@ -20,7 +20,10 @@ import android.databinding.tool.reflection.ModelAnalyzer;
import android.databinding.tool.reflection.ModelClass;
import android.databinding.tool.writer.KCode;
+import com.android.annotations.NonNull;
+
import java.util.List;
+import java.util.Objects;
public class ComparisonExpr extends Expr {
final String mOp;
@@ -102,4 +105,35 @@ public class ComparisonExpr extends Expr {
public String toString() {
return getLeft().toString() + ' ' + mOp + ' ' + getRight();
}
+
+ /**
+ * Returns true if this expression is a null check for the given expression.
+ * e.g. if this expression is a == null then this method returns true if invoked w/ a.
+ */
+ public boolean isNullCheckFor(@NonNull Expr expr) {
+ return "==".equals(mOp) && isNullabilityCheckFor(expr);
+ }
+
+ /**
+ * Returns true if this expression is a NOT NULL check for the given expression.
+ * e.g. if this expression is a != null then this method returns true if invoked w/ a.
+ */
+ public boolean isNotNullCheckFor(@NonNull Expr expr) {
+ return "!=".equals(mOp) && isNullabilityCheckFor(expr);
+ }
+
+ private boolean isNullabilityCheckFor(@NonNull Expr expr) {
+ if (isNullLiteral(getLeft())) {
+ return Objects.equals(expr.getUniqueKey(), getRight().getUniqueKey());
+ } else if (isNullLiteral(getRight())) {
+ return Objects.equals(expr.getUniqueKey(), getLeft().getUniqueKey());
+ }
+ return false;
+ }
+
+ private static boolean isNullLiteral(Expr expr) {
+ final ModelClass type = expr.getResolvedType();
+ return (type.isObject() && (expr instanceof SymbolExpr) &&
+ "null".equals(((SymbolExpr) expr).getText()));
+ }
}
diff --git a/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java b/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
index f517b8d1..545ca490 100644
--- a/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
+++ b/compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java
@@ -24,6 +24,7 @@ import android.databinding.tool.writer.KCode;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
+import java.util.Objects;
public class TernaryExpr extends Expr {
@@ -64,18 +65,35 @@ public class TernaryExpr extends Expr {
@Override
public void injectSafeUnboxing(ModelAnalyzer modelAnalyzer, ExprModel model) {
- if (getPred().getResolvedType().isNullable()) {
- safeUnboxChild(model, getPred());
+ final Expr pred = getPred();
+ if (pred.getResolvedType().isNullable()) {
+ safeUnboxChild(model, pred);
}
if (!getResolvedType().isNullable()) {
Expr ifTrue = getIfTrue();
Expr ifFalse = getIfFalse();
+ ComparisonExpr compPredicate = null;
+ if (pred instanceof ComparisonExpr) {
+ compPredicate = (ComparisonExpr) pred;
+ }
if (ifTrue.getResolvedType().isNullable()) {
- safeUnboxChild(model, ifTrue);
+ // check if this the case for
+ // a != null ? a : b
+ boolean guaranteedNotNull = compPredicate != null
+ && compPredicate.isNotNullCheckFor(ifTrue);
+ if (!guaranteedNotNull) {
+ safeUnboxChild(model, ifTrue);
+ }
}
if (ifFalse.getResolvedType().isNullable()) {
- safeUnboxChild(model, ifFalse);
+ // check if this the case for
+ // a == null ? b : a
+ boolean guaranteedNotNull = compPredicate != null
+ && compPredicate.isNullCheckFor(ifFalse);
+ if (!guaranteedNotNull) {
+ safeUnboxChild(model, ifFalse);
+ }
}
}
}