Add support for assumed return of null
This adds support for 'return null' for -assumevalues and
-assumenosideeffects rules.
Bug: 112438627
Change-Id: I6ccc3726c700b2500428b2504b9c67f7ab328875
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index f92eb80..a38e22b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -70,8 +70,8 @@
Instruction replacement = null;
ValueType valueType = instruction.outValue().outType();
if (rule != null && rule.hasReturnValue() && rule.getReturnValue().isSingleValue()) {
- assert valueType != ValueType.OBJECT;
Value value = code.createValue(valueType, instruction.getLocalInfo());
+ assert valueType != ValueType.OBJECT || rule.getReturnValue().isNull();
replacement = new ConstNumber(value, rule.getReturnValue().getSingleValue());
}
if (replacement == null &&
@@ -132,12 +132,12 @@
DexEncodedMethod definition = appInfo
.lookup(invoke.getType(), invokedMethod, callingContext);
- // Process invokes marked as having no side effects.
boolean invokeReplaced = false;
ProguardMemberRuleLookup lookup = lookupMemberRule(definition);
if (lookup != null) {
if (lookup.type == RuleType.ASSUME_NO_SIDE_EFFECTS
&& (invoke.outValue() == null || !invoke.outValue().isUsed())) {
+ // Remove invoke if marked as having no side effects and the return value is not used.
iterator.remove();
invokeReplaced = true;
} else if (invoke.outValue() != null && invoke.outValue().isUsed()) {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index d1a0b3d..740aa2c 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -968,6 +968,8 @@
ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(true));
} else if (acceptString("false")) {
ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue(false));
+ } else if (acceptString("null")) {
+ ruleBuilder.setReturnValue(new ProguardMemberRuleReturnValue());
} else {
TextPosition fieldOrValueStart = getPosition();
String qualifiedFieldNameOrInteger = acceptFieldNameOrIntegerForReturn();
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
index b4e9596..a1aad20 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardMemberRuleReturnValue.java
@@ -8,39 +8,60 @@
import com.android.tools.r8.utils.LongInterval;
public class ProguardMemberRuleReturnValue {
+ public enum Type {
+ BOOLEAN,
+ VALUE_RANGE,
+ FIELD,
+ NULL
+ }
+ private final Type type;
private final boolean booleanValue;
private final LongInterval longInterval;
private final DexField field;
ProguardMemberRuleReturnValue(boolean value) {
+ this.type = Type.BOOLEAN;
this.booleanValue = value;
this.longInterval = null;
this.field = null;
}
ProguardMemberRuleReturnValue(LongInterval value) {
+ this.type = Type.VALUE_RANGE;
this.booleanValue = false;
this.longInterval = value;
this.field = null;
}
ProguardMemberRuleReturnValue(DexField field) {
+ this.type = Type.FIELD;
this.booleanValue = false;
this.longInterval = null;
this.field = field;
}
+ ProguardMemberRuleReturnValue() {
+ this.type = Type.NULL;
+ this.booleanValue = false;
+ this.longInterval = null;
+ this.field = null;
+ }
+
public boolean isBoolean() {
- return longInterval == null && field == null;
+ return type == Type.BOOLEAN;
}
public boolean isValueRange() {
- return longInterval != null && field == null;
+ return type == Type.VALUE_RANGE;
}
public boolean isField() {
- return field != null;
+ return type == Type.FIELD;
+ }
+
+ public boolean isNull() {
+ return type == Type.NULL;
}
public boolean getBoolean() {
@@ -51,22 +72,27 @@
/**
* Returns if this return value is a single value.
*
- * Boolean values are considered a single value.
+ * Boolean values and null are considered a single value.
*/
public boolean isSingleValue() {
- return isBoolean() || (isValueRange() && longInterval.isSingleValue());
+ return isBoolean() || isNull() || (isValueRange() && longInterval.isSingleValue());
}
/**
* Returns the return value.
*
* Boolean values are returned as 0 for <code>false</code> and 1 for <code>true</code>.
+ *
+ * Reference value <code>null</code> is returned as 0.
*/
public long getSingleValue() {
assert isSingleValue();
if (isBoolean()) {
return booleanValue ? 1 : 0;
}
+ if (isNull()) {
+ return 0;
+ }
return longInterval.getSingleValue();
}
@@ -86,6 +112,8 @@
result.append(" return ");
if (isBoolean()) {
result.append(booleanValue ? "true" : "false");
+ } else if (isNull()) {
+ result.append("null");
} else if (isValueRange()) {
result.append(longInterval.getMin());
if (!isSingleValue()) {
diff --git a/src/test/examples/assumenosideeffects6/Assumenosideeffects.java b/src/test/examples/assumenosideeffects6/Assumenosideeffects.java
new file mode 100644
index 0000000..07630b9
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/Assumenosideeffects.java
@@ -0,0 +1,28 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects6;
+
+class A {
+ @CheckDiscarded
+ public static Object methodStaticNull() {
+ System.out.println("methodStaticNull");
+ return new Object();
+ }
+
+ // This method must be on a class which is not explicitly kept (as Assumenosideeffects is).
+ // For an explicitly kept class we cannot expect single target for any methods, so the rule
+ // will never apply (see b/70550443#comment2).
+ @CheckDiscarded
+ public Object methodNull() {
+ System.out.println("methodNull");
+ return new Object();
+ }
+}
+
+public class Assumenosideeffects {
+ public static void main(String[] args) {
+ System.out.println(A.methodStaticNull() != null ? "NOT NULL" : "NULL");
+ System.out.println((new A().methodNull()) != null ? "NOT NULL" : "NULL");
+ }
+}
diff --git a/src/test/examples/assumenosideeffects6/CheckDiscarded.java b/src/test/examples/assumenosideeffects6/CheckDiscarded.java
new file mode 100644
index 0000000..a249a6d
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/CheckDiscarded.java
@@ -0,0 +1,8 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package assumenosideeffects6;
+
+public @interface CheckDiscarded {
+
+}
diff --git a/src/test/examples/assumenosideeffects6/keep-rules-discard.txt b/src/test/examples/assumenosideeffects6/keep-rules-discard.txt
new file mode 100644
index 0000000..0f51575
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/keep-rules-discard.txt
@@ -0,0 +1,23 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects6.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to return null and have no side effects.
+-assumenosideeffects class assumenosideeffects6.A {
+ public static java.lang.Object methodStaticNull() return null;
+ public java.lang.Object methodNull() return null;
+}
+
+# Allow access modification to enable minification.
+-allowaccessmodification
+
+# Check that methods has been discarded.
+-checkdiscard class ** {
+ @assumenosideeffects6.CheckDiscarded *;
+}
diff --git a/src/test/examples/assumenosideeffects6/keep-rules.txt b/src/test/examples/assumenosideeffects6/keep-rules.txt
new file mode 100644
index 0000000..c158dd6
--- /dev/null
+++ b/src/test/examples/assumenosideeffects6/keep-rules.txt
@@ -0,0 +1,18 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep public class assumenosideeffects6.Assumenosideeffects {
+ public static void main(...);
+}
+
+# Mark some methods to return null and have no side effects.
+-assumenosideeffects class assumenosideeffects6.A {
+ public static java.lang.Object methodStaticNull() return null;
+ public java.lang.Object methodNull() return null;
+}
+
+# Allow access modification to enable minification.
+-allowaccessmodification
diff --git a/src/test/examples/assumevalues7/Assumevalues.java b/src/test/examples/assumevalues7/Assumevalues.java
new file mode 100644
index 0000000..e83fc2e
--- /dev/null
+++ b/src/test/examples/assumevalues7/Assumevalues.java
@@ -0,0 +1,30 @@
+// Copyright (c) 2018 the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package assumevalues7;
+
+class A {
+ public static Object getObjectStatic() {
+ return new Object();
+ }
+
+ // This method must be on a class which is not explicitly kept (as Assumevalues is).
+ // For an explicitly kept class we cannot expect single target for any methods, so the rule
+ // will never apply (see b/70550443#comment2).
+ public Object getObject() {
+ return new Object();
+ }
+}
+
+public class Assumevalues {
+ public static void main(String[] args) {
+ if (A.getObjectStatic() != null) {
+ System.out.println("NOPE_STATIC_NOT_NULL");
+ }
+ if (new A().getObject() != null) {
+ System.out.println("NOPE_NOT_NULL");
+ }
+ System.out.println("OK");
+ }
+}
diff --git a/src/test/examples/assumevalues7/keep-rules.txt b/src/test/examples/assumevalues7/keep-rules.txt
new file mode 100644
index 0000000..c8c6c2b
--- /dev/null
+++ b/src/test/examples/assumevalues7/keep-rules.txt
@@ -0,0 +1,15 @@
+# Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+# Keep the application entry point. Get rid of everything that is not
+# reachable from there.
+-keep class assumevalues7.Assumevalues {
+ void main(...);
+}
+
+# Mark some methods returning null.
+-assumevalues class assumevalues7.A {
+ public static java.lang.Object getObjectStatic() return null;
+ public java.lang.Object getObject() return null;
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
index f79e01f..a690ee8 100644
--- a/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ProguardConfigurationParserTest.java
@@ -417,12 +417,14 @@
assertTrue(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals(rule.getName().matches("returnsTrue"), rule.getReturnValue().getBoolean());
matches |= 1 << 0;
} else if (rule.getName().matches("returns1")) {
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertTrue(rule.getReturnValue().isSingleValue());
assertEquals(1, rule.getReturnValue().getValueRange().getMin());
assertEquals(1, rule.getReturnValue().getValueRange().getMax());
@@ -432,6 +434,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(2, rule.getReturnValue().getValueRange().getMin());
assertEquals(4, rule.getReturnValue().getValueRange().getMax());
@@ -440,6 +443,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(234, rule.getReturnValue().getValueRange().getMin());
assertEquals(567, rule.getReturnValue().getValueRange().getMax());
@@ -448,15 +452,23 @@
assertFalse(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertTrue(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
assertEquals("int", rule.getReturnValue().getField().type.toString());
assertEquals("X", rule.getReturnValue().getField().name.toString());
matches |= 1 << 4;
+ } else if (rule.getName().matches("returnsNull")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertTrue(rule.getReturnValue().isNull());
+ assertTrue(rule.getReturnValue().isSingleValue());
+ matches |= 1 << 5;
} else {
fail("Unexpected");
}
}
- assertEquals((1 << 5) - 1, matches);
+ assertEquals((1 << 6) - 1, matches);
}
@Test
@@ -474,12 +486,14 @@
assertTrue(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals(rule.getName().matches("isTrue"), rule.getReturnValue().getBoolean());
matches |= 1 << 0;
} else if (rule.getName().matches("is1")) {
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertTrue(rule.getReturnValue().isSingleValue());
assertEquals(1, rule.getReturnValue().getValueRange().getMin());
assertEquals(1, rule.getReturnValue().getValueRange().getMax());
@@ -489,6 +503,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(2, rule.getReturnValue().getValueRange().getMin());
assertEquals(4, rule.getReturnValue().getValueRange().getMax());
@@ -497,6 +512,7 @@
assertFalse(rule.getReturnValue().isBoolean());
assertTrue(rule.getReturnValue().isValueRange());
assertFalse(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertFalse(rule.getReturnValue().isSingleValue());
assertEquals(234, rule.getReturnValue().getValueRange().getMin());
assertEquals(567, rule.getReturnValue().getValueRange().getMax());
@@ -505,15 +521,23 @@
assertFalse(rule.getReturnValue().isBoolean());
assertFalse(rule.getReturnValue().isValueRange());
assertTrue(rule.getReturnValue().isField());
+ assertFalse(rule.getReturnValue().isNull());
assertEquals("com.google.C", rule.getReturnValue().getField().clazz.toString());
assertEquals("int", rule.getReturnValue().getField().type.toString());
assertEquals("X", rule.getReturnValue().getField().name.toString());
matches |= 1 << 4;
+ } else if (rule.getName().matches("isNull")) {
+ assertFalse(rule.getReturnValue().isBoolean());
+ assertFalse(rule.getReturnValue().isValueRange());
+ assertFalse(rule.getReturnValue().isField());
+ assertTrue(rule.getReturnValue().isNull());
+ assertTrue(rule.getReturnValue().isSingleValue());
+ matches |= 1 << 5;
} else {
fail("Unexpected");
}
}
- assertEquals((1 << 5) - 1, matches);
+ assertEquals((1 << 6) - 1, matches);
}
@Test
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java
new file mode 100644
index 0000000..578ad2a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumenosideeffects6Test.java
@@ -0,0 +1,66 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumenosideeffects6Test extends TreeShakingTest {
+
+ @Parameters(name = "mode:{0}-{1} minify:{2}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (MinifyMode minify : MinifyMode.values()) {
+ parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+ parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+ parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+ }
+ return parameters;
+ }
+
+ public TreeShakingAssumenosideeffects6Test(
+ Frontend frontend, Backend backend, MinifyMode minify) {
+ super(
+ "examples/assumenosideeffects6",
+ "assumenosideeffects6.Assumenosideeffects",
+ frontend,
+ backend,
+ minify);
+ }
+
+ @Test
+ public void testKeeprules() throws Exception {
+ runTest(
+ null,
+ TreeShakingAssumenosideeffects6Test::assumenosideeffects6CheckOutput,
+ null,
+ ImmutableList.of("src/test/examples/assumenosideeffects6/keep-rules.txt"));
+ }
+
+ @Test
+ public void testKeeprulesdiscard() throws Exception {
+ runTest(
+ null,
+ TreeShakingAssumenosideeffects6Test::assumenosideeffects6CheckOutput,
+ null,
+ ImmutableList.of("src/test/examples/assumenosideeffects6/keep-rules-discard.txt"));
+ }
+
+ private static void assumenosideeffects6CheckOutput(String output1, String output2) {
+ Assert.assertEquals(
+ StringUtils.lines("methodStaticNull", "NOT NULL", "methodNull", "NOT NULL"), output1);
+ Assert.assertEquals(StringUtils.lines("NULL", "NULL"), output2);
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java
new file mode 100644
index 0000000..789e40c
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/shaking/examples/TreeShakingAssumevalues7Test.java
@@ -0,0 +1,68 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.shaking.examples;
+
+import com.android.tools.r8.TestBase.MinifyMode;
+import com.android.tools.r8.shaking.TreeShakingTest;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.ConstStringInstructionSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject.JumboStringMode;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TreeShakingAssumevalues7Test extends TreeShakingTest {
+
+ @Parameters(name = "mode:{0}-{1} minify:{2}")
+ public static Collection<Object[]> data() {
+ List<Object[]> parameters = new ArrayList<>();
+ for (MinifyMode minify : MinifyMode.values()) {
+ parameters.add(new Object[] {Frontend.JAR, Backend.CF, minify});
+ parameters.add(new Object[] {Frontend.JAR, Backend.DEX, minify});
+ parameters.add(new Object[] {Frontend.DEX, Backend.DEX, minify});
+ }
+ return parameters;
+ }
+
+ public TreeShakingAssumevalues7Test(Frontend frontend, Backend backend, MinifyMode minify) {
+ super("examples/assumevalues7", "assumevalues7.Assumevalues", frontend, backend, minify);
+ }
+
+ @Test
+ public void test() throws Exception {
+ runTest(
+ getBackend() == Backend.DEX ? TreeShakingAssumevalues7Test::assumevalues7CheckCode : null,
+ TreeShakingAssumevalues7Test::assumevalues7CheckOutput,
+ null,
+ ImmutableList.of("src/test/examples/assumevalues7/keep-rules.txt"));
+ }
+
+ private static void assumevalues7CheckCode(CodeInspector inspector) {
+ inspector.forAllClasses(c -> {
+ c.forAllMethods(m -> {
+ if (m.getFinalName().equals("main")) {
+ m.iterateInstructions().forEachRemaining(i -> {
+ if (i.isConstString(JumboStringMode.ALLOW)) {
+ ConstStringInstructionSubject str = (ConstStringInstructionSubject) i;
+ assert !str.getString().toASCIIString().contains("NOPE");
+ }
+ });
+ }
+ });
+ });
+ }
+
+ private static void assumevalues7CheckOutput(String output1, String output2) {
+ Assert.assertEquals(StringUtils.lines("NOPE_STATIC_NOT_NULL", "NOPE_NOT_NULL", "OK"), output1);
+ Assert.assertEquals(StringUtils.lines("OK"), output2);
+ }
+}
\ No newline at end of file
diff --git a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
index 78d3683..b233ea3 100644
--- a/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
+++ b/src/test/proguard/valid/assume-no-side-effects-with-return-value.flags
@@ -9,4 +9,5 @@
public static int returns2To4() return 2..4;
public static int returns234To567() return 234..567;
public static int returnsField() return com.google.C.X;
+ public static Object returnsNull() return null;
}
diff --git a/src/test/proguard/valid/assume-values-with-return-value.flags b/src/test/proguard/valid/assume-values-with-return-value.flags
index 7f89a79..90cefbe 100644
--- a/src/test/proguard/valid/assume-values-with-return-value.flags
+++ b/src/test/proguard/valid/assume-values-with-return-value.flags
@@ -9,4 +9,5 @@
public static final int is2To4() return 2..4;
public static final int is234To567() return 234..567;
public static final int isField() return com.google.C.X;
+ public static final Object isNull() return null;
}
\ No newline at end of file