diff options
author | Yigit Boyar <yboyar@google.com> | 2017-12-28 17:13:44 -0800 |
---|---|---|
committer | Yigit Boyar <yboyar@google.com> | 2018-01-09 16:55:38 +0000 |
commit | c56acaa6c114d8a4a463b97f6b02b6a1d508f152 (patch) | |
tree | b4f07f1291578c6bae1a6eddc6a668c09301d765 /compiler/src/main/java/android/databinding | |
parent | 28efa96610b913ce5b30cc36ed33dbf1b62f3f66 (diff) | |
download | data-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.java | 34 | ||||
-rw-r--r-- | compiler/src/main/java/android/databinding/tool/expr/TernaryExpr.java | 26 |
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); + } } } } |