Fix EnumOrdinalizer.
EnumOrdinalizer incorrectly selects enums for ordinalization
(replacement of the enum values by integer ordinals) under a
few circumstances.
Also:
- fixed quite a few of the units tests.
- allow ordinalization even with access to values().
Bug: issue 8846.
Change-Id: I9c22c1576a238d5f6b3a5f52b25b2ca697b83fad
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
index 2d988f5..4232ab8 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java
@@ -37,6 +37,7 @@
import com.google.gwt.dev.jjs.ast.JModVisitor;
import com.google.gwt.dev.jjs.ast.JNewArray;
import com.google.gwt.dev.jjs.ast.JNonNullType;
+import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JPrimitiveType;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JStatement;
@@ -387,22 +388,6 @@
if (x.getInstance() != null) {
// check any instance field reference other than ordinal
blackListIfEnumExpression(x.getInstance());
- } else if (x.getField().isStatic()) {
- /*
- * Black list if the $VALUES static field is referenced, unless it's
- * within the auto-generated clinit or values method, within the enum
- * class itself.
- *
- * TODO (jbrosenberg): Investigate further whether referencing the
- * $VALUES array (as well as the values() method) should not block
- * ordinalization. Instead, convert $VALUES to an array of int.
- */
- if (x.getField().getName().equals(JEnumType.VALUES_ARRAY_NAME)
- && ((this.currentMethod.getEnclosingType() != x.getField().getEnclosingType()) ||
- (!this.currentMethod.getName().equals("values") &&
- !this.currentMethod.getName().equals("$clinit")))) {
- blackListIfEnum(x.getField().getEnclosingType(), x.getSourceInfo());
- }
}
}
@@ -429,9 +414,14 @@
} else if (x.getTarget().isStatic()) {
// black-list static method calls on an enum class only for valueOf()
// and values()
- String methodName = x.getTarget().getName();
- if (methodName.equals("valueOf") || methodName.equals("values")) {
- blackListIfEnum(x.getTarget().getEnclosingType(), x.getSourceInfo());
+ JMethod target = x.getTarget();
+ maybeBlackListDueToStaticCall(x.getSourceInfo(), target);
+ }
+
+ if (x.getTarget().isNative()) {
+ // Black list enum types declared in parameters of native functions.
+ for (JParameter parameter :x.getTarget().getParams()) {
+ blackListIfEnum(parameter.getType(), x.getSourceInfo());
}
}
@@ -478,15 +468,12 @@
if (x.getInstance() != null) {
blackListIfEnumExpression(x.getInstance());
} else if (x.getTarget().isStatic()) {
- /*
- * need to exempt static methodCalls for an enum class if it occurs
- * within the enum class itself (such as in $clinit() or values())
- */
- if (this.currentMethod.getEnclosingType() != x.getTarget().getEnclosingType()) {
- blackListIfEnum(x.getTarget().getEnclosingType(), x.getSourceInfo());
- }
+ maybeBlackListDueToStaticCall(x.getSourceInfo(), x.getTarget());
}
+ // Black list enums returned to JSNI.
+ blackListIfEnum(x.getTarget().getType(), x.getSourceInfo());
+
// defer to ImplicitUpcastAnalyzer to check method call args & params
super.endVisit(x, ctx);
}
@@ -608,7 +595,18 @@
blackListIfEnum(instance.getType(), instance.getSourceInfo());
}
}
+
+ /**
+ * Blacklist the enum if there is a call to either MyEnum.valueOf() or MyEnum.values().
+ */
+ private void maybeBlackListDueToStaticCall(SourceInfo info, JMethod target) {
+ if (target.getEnclosingType().isEnumOrSubclass() != null &&
+ (target.getName().equals("valueOf") || target.getName().equals("values"))) {
+ blackListIfEnum(target.getEnclosingType(), info);
+ }
+ }
}
+
/**
* A visitor which replaces enum types with an integer.
*
@@ -730,20 +728,20 @@
int removeIndex = 0;
// Make a copy to avoid concurrent modification.
for (JStatement stmt : new ArrayList<JStatement>(block.getStatements())) {
- if (stmt instanceof JDeclarationStatement) {
- JVariableRef ref = ((JDeclarationStatement) stmt).getVariableRef();
- if (ref instanceof JFieldRef) {
- JFieldRef enumRef = (JFieldRef) ref;
- // See if LHS is a field ref to the class being initialized.
- JField field = enumRef.getField();
- if (field.isStatic() && field.getEnclosingType() == enclosingType) {
- if (field instanceof JEnumField ||
- field.getName().equals(JEnumType.VALUES_ARRAY_NAME)) {
- block.removeStmt(removeIndex--);
- field.setInitializer(null);
- }
- }
- }
+ if (!(stmt instanceof JDeclarationStatement)) {
+ continue;
+ }
+ JVariableRef ref = ((JDeclarationStatement) stmt).getVariableRef();
+ if (!(ref instanceof JFieldRef)) {
+ continue;
+ }
+
+ // See if LHS is a field ref to the class being initialized.
+ JField field = ((JFieldRef) ref).getField();
+ if (field.isStatic() && field.getEnclosingType() == enclosingType &&
+ field instanceof JEnumField) {
+ block.removeStmt(removeIndex--);
+ field.setInitializer(null);
}
++removeIndex;
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
index 1bc9168..d6957e3 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/GwtAstBuilder.java
@@ -2189,7 +2189,7 @@
}
private JField createEnumValuesField(JEnumType type) {
- // $VALUES = new E[]{A,B,B};
+ // $VALUES = new E[]{A,B,C};
JArrayType enumArrayType = new JArrayType(type);
JField valuesField = new JField(type.getSourceInfo(), JEnumType.VALUES_ARRAY_NAME, type,
enumArrayType, true, Disposition.FINAL);
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 b2f1795..fa09708 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -167,14 +167,17 @@
"import com.google.gwt.core.client.JavaScriptObject;",
"public abstract class Enum<E extends Enum<E>> implements Serializable {",
" public static native <T extends Enum<T>> T valueOf(Class<T> enumType,",
- " String name) /*-{ }-*/;",
+ " String name) /*-{ return enumType + name; }-*/;",
+ " public static native <T extends Enum<T>> T valueOf(JavaScriptObject enumType,",
+ " String name) /*-{ return enumType + name; }-*/;",
" protected static native <T extends Enum<T>> JavaScriptObject createValueOfMap(",
" T[] enumConstants) /*-{ }-*/;",
- " protected static native <T extends Enum<T>> T valueOf(JavaScriptObject map,",
- " String name) /*-{ }-*/;",
- " protected Enum(String name, int ordinal) { ", " this.name = name;",
- " this.ordinal = ordinal;}", " private final String name;",
- " private final int ordinal;", " public final String name() { return name; }",
+ " protected Enum(String name, int ordinal) { ",
+ " this.name = name;",
+ " this.ordinal = ordinal;}",
+ " private final String name;",
+ " private final int ordinal;",
+ " public final String name() { return name; }",
" public final int ordinal() { return ordinal; }",
"}"
);
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java b/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
index 2d4710d..a653128 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java
@@ -21,20 +21,34 @@
import com.google.gwt.dev.jjs.ast.JProgram;
/**
- * A set of tests for the conditions under which ordinalization is and is not
- * allowed. The ordinalization is performed when allowed.
+ * A set of tests for the conditions under which ordinalization is and is not allowed. The
+ * ordinalization is performed when allowed.
*
- * A complete test of the resulting ordinalization is not performed. However,
- * the ImplementCastsAndTypeChecks and the EqualityNormalizer are run after the EnumOrdinalizer,
- * to help ensure the integrity of the AST, such that there are no partially
- * mismatched type assignments or comparisons, and that no binary operations
- * between a primitive type and null have been added. Typically, such errors
- * introduced by the EnumOrdinalizer are caught by these normalizers, so it
- * makes sense to test the output in this way. Thus, we provide confidence
- * that the AST is left in a coherent state, but it is not a complete test that
- * ordinalization has completed correctly in every respec.
+ * A complete test of the resulting ordinalization is not performed. However, the
+ * ImplementCastsAndTypeChecks and the EqualityNormalizer are run after the EnumOrdinalizer, to help
+ * ensure the integrity of the AST, such that there are no partially mismatched type assignments or
+ * comparisons, and that no binary operations between a primitive type and null have been added.
+ * Typically, such errors introduced by the EnumOrdinalizer are caught by these normalizers, so it
+ * makes sense to test the output in this way. Thus, we provide confidence that the AST is left in
+ * a coherent state, but it is not a complete test that ordinalization has completed correctly in
+ * every respec.
*/
public class EnumOrdinalizerTest extends OptimizerTestBase {
+ /*
+ * Always run ImplementCastsAndTypeChecks and EqualityNormalizer, even in cases where we
+ * are testing that ordinalization cannot occur, since there may be other
+ * enums (such as DummyEnum) which do get ordinalized, and we want to test
+ * that all is well regardless.
+ */
+ private final boolean performCastReplacement = true;
+ private final boolean runEqualityNormalizer = true;
+ // These are enabled as needed for a given test
+ private boolean runMakeCallsStatic;
+ private boolean runMethodInliner;
+ private boolean runMethodCallTightener;
+ private boolean runPruner;
+ private boolean runTypeTightener;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -46,6 +60,11 @@
runMethodCallTightener = false;
runMethodInliner = true;
runMakeCallsStatic = true;
+ // Opportunities for ordinalization are only present after unused references to $VALUES are
+ // pruned.
+ // NOTE: because we are pruning, each test case needs to make sure that enums that are
+ // considered for ordinalization are still live.
+ runPruner = true;
}
@Override
@@ -54,45 +73,45 @@
}
public void testOrdinalizeBasicAssignment()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit apple = Fruit.APPLE;",
- "Fruit orange = Fruit.ORANGE;");
+ "Fruit orange = Fruit.ORANGE;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizeNewArrayAndAssignmentLocalRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit[] fruits = new Fruit[] {Fruit.APPLE, Fruit.ORANGE, Fruit.APPLE};",
- "if (fruits[0] == Fruit.APPLE) {",
- " fruits[0] = Fruit.ORANGE;",
- "}");
+ "if (fruits[0] == Fruit.APPLE) {",
+ " fruits[0] = Fruit.ORANGE;",
+ "}");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizeNewArrayOfArrayAndAssignmentLocalRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit[][] fruits = new Fruit[][] ",
- " {{Fruit.APPLE, Fruit.ORANGE},{Fruit.APPLE, Fruit.ORANGE}};",
- "if (fruits[0][1] == Fruit.APPLE) {",
- " fruits[0][1] = Fruit.ORANGE;",
- "}");
+ " {{Fruit.APPLE, Fruit.ORANGE},{Fruit.APPLE, Fruit.ORANGE}};",
+ "if (fruits[0][1] == Fruit.APPLE) {",
+ " fruits[0][1] = Fruit.ORANGE;",
+ "}");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizeNewArrayAndAssignmentFieldRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("private final Fruit[] fruits = new Fruit[] ",
- " {Fruit.APPLE, Fruit.ORANGE, Fruit.APPLE};");
+ " {Fruit.APPLE, Fruit.ORANGE, Fruit.APPLE};");
optimize("void", "EntryPoint ep = new EntryPoint();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
@@ -100,12 +119,12 @@
}
public void testOrdinalizableFinalFieldUninitializedByDefault()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("private final Fruit uninitializedFinalFruit;",
- "public EntryPoint() {",
- " uninitializedFinalFruit = Fruit.ORANGE;",
- "}");
+ "public EntryPoint() {",
+ " uninitializedFinalFruit = Fruit.ORANGE;",
+ "}");
optimize("void", "EntryPoint ep = new EntryPoint();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
@@ -114,18 +133,18 @@
}
public void testOrdinalizeSwitchStatement()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
setupFruitSwitchMethod();
optimize("void", "String apple = fruitSwitch(Fruit.APPLE);",
- "String orange = fruitSwitch(Fruit.ORANGE);");
+ "String orange = fruitSwitch(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizeIfStatement()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl(
"public static String fruitIf(Fruit fruit) {",
@@ -138,14 +157,14 @@
" }",
"}");
optimize("void", "String apple = fruitIf(Fruit.APPLE);",
- "String orange = fruitIf(Fruit.ORANGE);");
+ "String orange = fruitIf(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizeConditional()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit = (true) ? Fruit.APPLE : Fruit.ORANGE;");
@@ -154,7 +173,7 @@
}
public void testOrdinalizeFieldRefOrdinalMethodCall()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "int i = Fruit.APPLE.ordinal();");
@@ -163,10 +182,10 @@
}
public void testOrdinalizeVariableRefOrdinalMethodCall()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "int i = fruit.ordinal();");
+ "int i = fruit.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
@@ -178,7 +197,8 @@
optimize("void", "EmptyEnum myEnum;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
- assertTrue(tracker.isOrdinalized("test.EntryPoint$EmptyEnum"));
+ assertTrue(tracker.isOrdinalized("test.EntryPoint$EmptyEnum") ||
+ !tracker.isVisited("test.EntryPoint$EmptyEnum"));
}
public void testOrdinalizeUnusedEnum() throws UnableToCompleteException {
@@ -187,84 +207,98 @@
optimize("void", "Fruit myEnum;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
- assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
+ assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit") ||
+ !tracker.isVisited("test.EntryPoint$Fruit"));
}
public void testOrdinalizeMethodCallExpressionOrdinalFieldRef()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static Fruit getResolvedFruit(Fruit fruit) {",
- " if (fruit == Fruit.APPLE) {",
- " return Fruit.ORANGE;",
- " } else { ",
- " return Fruit.APPLE;",
- " }",
- "}");
+ " if (fruit == Fruit.APPLE) {",
+ " return Fruit.ORANGE;",
+ " } else { ",
+ " return Fruit.APPLE;",
+ " }",
+ "}");
addSnippetClassDecl("public static int switchMethodCall(Fruit fruit) {",
- " int retVal = 0;",
- " switch (getResolvedFruit(fruit)) {",
- " case APPLE: retVal = 12; break;",
- " case ORANGE:retVal = 73; break;",
- " }",
- " return retVal;",
- "}");
+ " int retVal = 0;",
+ " switch (getResolvedFruit(fruit)) {",
+ " case APPLE: retVal = 12; break;",
+ " case ORANGE:retVal = 73; break;",
+ " }",
+ " return retVal;",
+ "}");
optimize("void", "int i = switchMethodCall(Fruit.APPLE);",
- "Fruit fruit = Fruit.ORANGE;",
- "int j = switchMethodCall(fruit);");
+ "Fruit fruit = Fruit.ORANGE;",
+ "int j = switchMethodCall(fruit);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizableStaticFieldRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// this will cause a static field ref in the enum clinit
setupFruitEnumWithStaticField();
- optimize("void", "String y = Fruit.staticField;");
+ optimize("void", "String y = Fruit.staticField + Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testOrdinalizableStaticMethod()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// this will cause a static method enum class
setupFruitEnumWithStaticMethod();
- optimize("void", "int y = Fruit.staticMethod();");
+ optimize("void", "int y = Fruit.staticMethod() + Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
- public void testNotOrdinalizableStaticMethodThatRefsValuesArray()
- throws UnableToCompleteException {
- // this will cause a static method that references an element
- // of the values() array
- setupFruitEnumWithStaticMethodThatRefsValuesArray();
- optimize("void", "Fruit y = Fruit.forInteger(0);");
+ public void testOrdinalizableCallingValues()
+ throws UnableToCompleteException {
+ setupFruitEnum();
+ optimize("void", "int l = Fruit.values().length;",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
- assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
+ assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
- public void testNotOrdinalizableStaticMethodThatRefsValuesLength()
- throws UnableToCompleteException {
- // this will cause a static method that references values().length
- setupFruitEnumWithStaticMethodThatRefsValuesLength();
- optimize("void", "Fruit y = Fruit.forInteger(0);");
+ public void testOrdinalizableStaticFieldRefToVALUES()
+ throws UnableToCompleteException {
+ // this ends up inlining the values() method call, and thus $VALUES is referenced external
+ // to the Fruit enum class.
+ setupFruitEnum();
+ optimize("void", "Fruit[] fruits = Fruit.values();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
- assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
+ assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
+ }
+
+ public void testOrdinalizableStaticMethodThatRefsValuesLength()
+ throws UnableToCompleteException {
+ // this will cause a static method that references values().length
+ setupFruitEnumWithStaticMethodThatRefsValuesLength();
+ optimize("void", "Fruit y = Fruit.forInteger(0);",
+ "int ord = Fruit.APPLE.ordinal();");
+
+ EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
+ assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
+ assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
public void testNotOrdinalizableInstanceStaticFieldRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// this will cause a static field ref in the enum clinit
setupFruitEnumWithStaticField();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "String y = fruit.staticField;");
+ "String y = fruit.staticField;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -272,11 +306,11 @@
}
public void testNotOrdinalizableInstanceStaticMethod()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// this will cause a static method enum class
setupFruitEnumWithStaticMethod();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "int y = fruit.staticMethod();");
+ "int y = fruit.staticMethod();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -284,10 +318,10 @@
}
public void testNotOrdinalizableClassLiteralReference()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Class clazz = Fruit.class;",
- "String clazzStr = clazz.toString();");
+ "String clazzStr = clazz.toString() + Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -295,10 +329,10 @@
}
public void testNotOrdinalizableEnumValueOfWithClassLiteralArg()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Object Carrot = Enum.valueOf(Fruit.class, \"APPLE\");",
- "String carrot = Carrot.toString();");
+ "String carrot = Carrot.toString() + Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -306,10 +340,10 @@
}
public void testNotOrdinalizableGetClassMethodCall()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Class clazz = Fruit.APPLE.getClass();",
- "String clazzStr = clazz.toString();");
+ "String clazzStr = clazz.toString() + Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -317,10 +351,11 @@
}
public void testNotOrdinalizableExplicitCastToEnumClass()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Object obj = new Object();",
- "Fruit fruit = (Fruit) obj;");
+ "Fruit fruit = (Fruit) obj;",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -328,10 +363,11 @@
}
public void testNotOrdinalizableExplicitCastToArrayOfEnumClass()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Enum[] enumArray = new Enum[10];",
- "Fruit[] fruitArray = (Fruit[]) enumArray;");
+ "Fruit[] fruitArray = (Fruit[]) enumArray;",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -339,10 +375,11 @@
}
public void testNotOrdinalizableExplicitCastFromArrayOfEnumClass()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit[] fruitArray = new Fruit[10];",
- "Enum[] enumArray = (Enum[]) fruitArray;");
+ "Enum[] enumArray = (Enum[]) fruitArray;",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -350,10 +387,11 @@
}
public void testNotOrdinalizableExplicitCastToArrayOfArrayOfEnumClass()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Enum[][] enumArray = new Enum[10][10];",
- "Fruit[][] fruitArray = (Fruit[][]) enumArray;");
+ "Fruit[][] fruitArray = (Fruit[][]) enumArray;",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -361,10 +399,11 @@
}
public void testNotOrdinalizableExplicitCastFromArrayOfArrayOfEnumClass()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit[][] fruitArray = new Fruit[10][10];",
- "Enum[][] enumArray = (Enum[][]) fruitArray;");
+ "Enum[][] enumArray = (Enum[][]) fruitArray;",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -372,10 +411,10 @@
}
public void testNotOrdinalizableExplicitCastFromEnumClass()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Enum Carrot = (Enum) Fruit.APPLE;",
- "String carrot = Carrot.toString();");
+ "String carrot = Carrot.toString();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -383,10 +422,11 @@
}
public void testNotOrdinalizableOrdinalMethodRefFromExplicitCastWithBlackListableSubExpression()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "int ord = " +
- "((Fruit) Enum.valueOf(Fruit.class,\"APPLE\")).ordinal();");
+ "((Fruit) Enum.valueOf(Fruit.class,\"APPLE\")).ordinal();",
+ "int ord2 = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -394,10 +434,10 @@
}
public void testNotOrdinalizableInstanceFieldRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// this will cause an instance field ref in the enum constructor
setupFruitEnumWithInstanceField();
- optimize("void");
+ optimize("void", "String instanceField = Fruit.APPLE.instanceField;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -405,12 +445,12 @@
}
public void testNotOrdinalizableInstanceOfEnumExpression()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "if (fruit instanceof Enum) {",
- " fruit = Fruit.ORANGE;",
- "}");
+ "if (fruit instanceof Enum) {",
+ " fruit = Fruit.ORANGE;",
+ "}");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -418,24 +458,13 @@
}
public void testNotOrdinalizableInstanceOfEnumTestType()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Object fruitObj = new Object();",
- "if (fruitObj instanceof Fruit) {",
- " fruitObj = null;",
- "}");
-
- EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
- assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
- assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
- }
-
- public void testNotOrdinalizableStaticFieldRefToVALUES()
- throws UnableToCompleteException {
- // this ends up inlining the values() method call, and thus $VALUES is referenced external
- // to the Fruit enum class.
- setupFruitEnum();
- optimize("void", "Fruit[] fruits = Fruit.values();");
+ "if (fruitObj instanceof Fruit) {",
+ " fruitObj = null;",
+ "}",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -443,12 +472,13 @@
}
public void testNotOrdinalizableStaticMethodCallValues()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// make sure values() method call doesn't doesn't get inlined
runMethodInliner = false;
setupFruitEnum();
- optimize("void", "Fruit[] fruits = Fruit.values();");
+ optimize("void", "Fruit[] fruits = Fruit.values();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -456,14 +486,15 @@
}
public void testNotOrdinalizableJsniFieldRef()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static Fruit instanceFruit;");
addSnippetClassDecl("public static native void jsniMethod() /*-{",
- " var x = @test.EntryPoint::instanceFruit",
- "}-*/");
+ " var x = @test.EntryPoint::instanceFruit",
+ "}-*/");
optimize("void", "instanceFruit = Fruit.APPLE;",
- "jsniMethod();");
+ "jsniMethod();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -471,12 +502,13 @@
}
public void testNotOrdinalizableJsniFieldRefStatic()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void jsniMethod() /*-{",
- " var x = @test.EntryPoint.Fruit::APPLE",
- "}-*/");
- optimize("void", "jsniMethod();");
+ " var x = @test.EntryPoint.Fruit::APPLE",
+ "}-*/");
+ optimize("void", "jsniMethod();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -484,12 +516,13 @@
}
public void testNotOrdinalizableJsniFieldRefClassLiteral()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void jsniMethod() /*-{",
- " var x = @test.EntryPoint.Fruit::class",
- "}-*/");
- optimize("void", "jsniMethod();");
+ " var x = @test.EntryPoint.Fruit::class",
+ "}-*/");
+ optimize("void", "jsniMethod();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -497,10 +530,12 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpAssignment()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
- optimize("void", "Enum tomato;",
- "tomato = Fruit.APPLE;");
+ optimize("void",
+ "Enum tomato;",
+ "tomato = Fruit.APPLE;",
+ "int ord = tomato.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -508,12 +543,13 @@
}
public void testNotOrdinalizableImplicitUpcastFieldInitializedWithNullByDefault()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("static private Fruit uninitializedFruitAsNull;");
optimize("void", "if (uninitializedFruitAsNull != Fruit.APPLE) {",
- " uninitializedFruitAsNull = Fruit.ORANGE;",
- "}");
+ " uninitializedFruitAsNull = Fruit.ORANGE;",
+ "}",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -521,12 +557,12 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpEquals()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
setupVegetableEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "Enum carrot = (Enum) Vegetable.CARROT;",
- "boolean test = (fruit == carrot);");
+ "Enum carrot = (Enum) Vegetable.CARROT;",
+ "boolean test = (fruit == carrot);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -536,13 +572,13 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpNotEquals()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
setupVegetableEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "Enum carrot = (Enum) Vegetable.CARROT;",
- // do in opposite order from OpEquals test
- "boolean test = (carrot != fruit);");
+ "Enum carrot = (Enum) Vegetable.CARROT;",
+ // do in opposite order from OpEquals test
+ "boolean test = (carrot != fruit);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -552,17 +588,17 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpEqualsNull()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static boolean testIsNull(Fruit fruit) {",
- " if (fruit == null) {",
- " return true;",
- " } else {",
- " return false;",
- " }",
- "}");
+ " if (fruit == null) {",
+ " return true;",
+ " } else {",
+ " return false;",
+ " }",
+ "}");
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);");
+ "boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -570,17 +606,17 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpNotEqualsNull()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static boolean testIsNull(Fruit fruit) {",
- " if (fruit != null) {",
- " return true;",
- " } else {",
- " return false;",
- " }",
- "}");
+ " if (fruit != null) {",
+ " return true;",
+ " } else {",
+ " return false;",
+ " }",
+ "}");
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);");
+ "boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -588,10 +624,10 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcat()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "String str = \"A string followed by \" + fruit;");
+ "String str = \"A string followed by \" + fruit;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -599,10 +635,10 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcat2()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "String str = fruit + \" followed by a string\";");
+ "String str = fruit + \" followed by a string\";");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -610,11 +646,11 @@
}
public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcatAssignment()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit = Fruit.APPLE;",
- "String str = \"A string concatenated with: \";",
- "str += fruit;");
+ "String str = \"A string concatenated with: \";",
+ "str += fruit;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -622,9 +658,10 @@
}
public void testNotOrdinalizableImplicitUpcastDeclarationToNull()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
- optimize("void", "Fruit fruit = null;");
+ optimize("void", "Fruit fruit = null;",
+ "int ord = fruit == null ? Fruit.APPLE.ordinal() : fruit.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -632,10 +669,11 @@
}
public void testNotOrdinalizableImplicitUpcastAssignmentToNull()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Fruit fruit;",
- "fruit = null;");
+ "fruit = null;",
+ "int ord = fruit == null ? Fruit.APPLE.ordinal() : fruit.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -643,9 +681,10 @@
}
public void testNotOrdinalizableImplicitUpcastDeclaration()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
- optimize("void", "Enum tomato = Fruit.APPLE;");
+ optimize("void", "Enum tomato = Fruit.APPLE;",
+ "int ord = Fruit.APPLE.ordinal() + tomato.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -653,21 +692,22 @@
}
public void testNotOrdinalizableImplicitUpcastConditional()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
setupVegetableEnum();
optimize("void", "Enum tomato = null;",
- "tomato = (true) ? Fruit.APPLE : Vegetable.CARROT;");
+ "tomato = (true) ? Fruit.APPLE : Vegetable.CARROT;",
+ "int ord = Fruit.APPLE.ordinal() + Vegetable.CARROT.ordinal() + tomato.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertTrue(tracker.isVisited("test.EntryPoint$Vegetable"));
- assertFalse(tracker.isOrdinalized("test.EntryPoint$Vegetable"));
+ assertTrue(tracker.isOrdinalized("test.EntryPoint$Vegetable"));
}
public void testNotOrdinalizableImplicitUpcastOverriddenMethodReturnType()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
// this test depends on the tighteners running
runTypeTightener = true;
@@ -679,31 +719,31 @@
* overridden method type.
*/
addSnippetClassDecl("public interface EnumInterface {",
- " String name();",
- "}");
+ " String name();",
+ "}");
addSnippetClassDecl("public abstract class AbstractClass<T extends EnumInterface> {",
- " public abstract T getEnumClass();",
- "}");
+ " public abstract T getEnumClass();",
+ "}");
addSnippetClassDecl("public class CustomClass1 extends AbstractClass<EnumClass1> {",
- " public EnumClass1 getEnumClass() { return EnumClass1.CONST1; }",
- "}");
+ " public EnumClass1 getEnumClass() { return EnumClass1.CONST1; }",
+ "}");
addSnippetClassDecl("public class CustomClass2 extends AbstractClass<EnumClass2> {",
- " public EnumClass2 getEnumClass() { return EnumClass2.CONST2; }",
- "}");
+ " public EnumClass2 getEnumClass() { return EnumClass2.CONST2; }",
+ "}");
addSnippetClassDecl("public enum EnumClass1 implements EnumInterface {",
- " CONST1;",
- "}");
+ " CONST1;",
+ "}");
addSnippetClassDecl("public enum EnumClass2 implements EnumInterface {",
- " CONST2;",
- "}");
+ " CONST2;",
+ "}");
addSnippetClassDecl("public static void testEnumClass(AbstractClass abstractClass) {",
- " EnumInterface enumClass = abstractClass.getEnumClass();",
- "}");
+ " EnumInterface enumClass = abstractClass.getEnumClass();",
+ "}");
optimize("void", "EntryPoint ep = new EntryPoint();",
- "AbstractClass abstractClass1 = ep.new CustomClass1();",
- "AbstractClass abstractClass2 = ep.new CustomClass2();",
- "testEnumClass(abstractClass1);",
- "testEnumClass(abstractClass2);");
+ "AbstractClass abstractClass1 = ep.new CustomClass1();",
+ "AbstractClass abstractClass2 = ep.new CustomClass2();",
+ "testEnumClass(abstractClass1);",
+ "testEnumClass(abstractClass2);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$EnumClass1"));
@@ -713,18 +753,18 @@
}
public void testNotOrdinalizableImplicitUpcastMethodCallArgs()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static String getEnumString(Enum myEnum) {",
- // make sure this method does something so not inlined
- " int ord = myEnum.ordinal();",
- " String retString = \"\";",
- " for (int i = 0;i<ord;i++) {",
- " retString += \"-\";",
- " }",
- " retString += myEnum.name();",
- " return retString;",
- "}");
+ // make sure this method does something so not inlined
+ " int ord = myEnum.ordinal();",
+ " String retString = \"\";",
+ " for (int i = 0;i<ord;i++) {",
+ " retString += \"-\";",
+ " }",
+ " retString += myEnum.name();",
+ " return retString;",
+ "}");
optimize("void", "String stringApple = getEnumString(Fruit.APPLE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
@@ -733,16 +773,17 @@
}
public void testNotOrdinalizableImplicitUpcastMethodCallArgsNewArray()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static String getEnumString(Enum[] myEnumArray) {",
- " String retString = \"\";",
- " for (Enum myEnum : myEnumArray) {",
- " retString += myEnum.name();",
- " }",
- " return retString;",
- "}");
- optimize("void", "String stringFruits = getEnumString(new Enum[] {Fruit.APPLE, Fruit.ORANGE});");
+ " String retString = \"\";",
+ " for (Enum myEnum : myEnumArray) {",
+ " retString += myEnum.name();",
+ " }",
+ " return retString;",
+ "}");
+ optimize("void",
+ "String stringFruits = getEnumString(new Enum[] {Fruit.APPLE, Fruit.ORANGE});");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -750,15 +791,15 @@
}
public void testNotOrdinalizableImplicitUpcastMethodCallVarArgs()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static String getEnumString(Enum...myEnumArray) {",
- " String retString = \"\";",
- " for (Enum myEnum : myEnumArray) {",
- " retString += myEnum.name();",
- " }",
- " return retString;",
- "}");
+ " String retString = \"\";",
+ " for (Enum myEnum : myEnumArray) {",
+ " retString += myEnum.name();",
+ " }",
+ " return retString;",
+ "}");
optimize("void", "String stringFruits = getEnumString(Fruit.APPLE, Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
@@ -767,7 +808,7 @@
}
public void testNotOrdinalizableImplicitUpcastNewArrayElements()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
optimize("void", "Enum[] enums = new Enum[] {Fruit.APPLE, Fruit.ORANGE};");
@@ -777,20 +818,21 @@
}
public void testNotOrdinalizableImplicitUpcastNewArrayArrayElements()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
- optimize("void", "Enum[][] enums = new Enum[][] {{Fruit.APPLE, Fruit.ORANGE},{Fruit.ORANGE, Fruit.APPLE}};");
+ optimize("void",
+ "Enum[][] enums = new Enum[][] {{Fruit.APPLE, Fruit.ORANGE},{Fruit.ORANGE, Fruit.APPLE}};");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
- public void testNotOrdinalizableImplicitUpcastJsniMethodBodyParams()
- throws UnableToCompleteException {
+ public void testNotOrdinalizableJsniMethodBodyParams()
+ throws UnableToCompleteException {
setupFruitEnum();
- addSnippetClassDecl("public static native void passEnumToJsniMethod(Fruit myEnum) /*-{",
- "}-*/");
+ addSnippetClassDecl("public static native void passEnumToJsniMethod(Enum myEnum) /*-{",
+ " myEnum == null; }-*/");
optimize("void", "passEnumToJsniMethod(Fruit.APPLE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
@@ -798,14 +840,29 @@
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
}
- public void testNotOrdinalizableImplicitUpcastJsniMethodBodyReturnType()
- throws UnableToCompleteException {
+ public void testNotOrdinalizableImplicitUpcastJsniMethodBodyParams()
+ throws UnableToCompleteException {
setupFruitEnum();
- addSnippetClassDecl("public static native Fruit returnFruitViaJsni() /*-{",
- " var myJso;",
- " return myJso;",
- "}-*/");
- optimize("void", "Fruit fruit = returnFruitViaJsni();");
+ addSnippetClassDecl("public static native void passEnumToJsniMethod(Fruit myEnum) /*-{",
+ " myEnum == null; }-*/;");
+ optimize("void", "passEnumToJsniMethod(Fruit.APPLE);");
+
+ EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
+ assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
+ assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
+ }
+
+ public void testNotOrdinalizableJsniMethodBodyCall()
+ throws UnableToCompleteException {
+ setupFruitEnum();
+ addSnippetClassDecl("public static native void consumeFruitViaJsni() /*-{",
+ " var myJso = @test.EntryPoint::calledFromJsni(*)();",
+ "}-*/;",
+ "public static Fruit calledFromJsni() {",
+ " return Fruit.APPLE;",
+ "}");
+ optimize("void", "consumeFruitViaJsni();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -813,14 +870,15 @@
}
public void testNotOrdinalizableImplicitUpcastJsniMethodRefParams()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
setupFruitSwitchMethod();
addSnippetClassDecl("public static native void fruitSwitchViaJsni() /*-{",
- " var myJso;",
- " var result = @test.EntryPoint::fruitSwitch(Ltest/EntryPoint$Fruit;)(myJso);",
- "}-*/");
- optimize("void", "fruitSwitchViaJsni();");
+ " var myJso;",
+ " var result = @test.EntryPoint::fruitSwitch(Ltest/EntryPoint$Fruit;)(myJso);",
+ "}-*/");
+ optimize("void", "fruitSwitchViaJsni();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -828,15 +886,16 @@
}
public void testNotOrdinalizableImplicitUpcastJsniMethodRefReturnType()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static Fruit returnSomeFruit() {",
- " return Fruit.APPLE;",
- "}");
+ " return Fruit.APPLE;",
+ "}");
addSnippetClassDecl("public static native void jsniMethodRefWithEnumReturn() /*-{",
- " var result = @test.EntryPoint::returnSomeFruit()();",
- "}-*/");
- optimize("void", "jsniMethodRefWithEnumReturn();");
+ " var result = @test.EntryPoint::returnSomeFruit()();",
+ "}-*/");
+ optimize("void", "jsniMethodRefWithEnumReturn();",
+ "int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -844,19 +903,20 @@
}
public void testNotOrdinalizableImplicitUpcastReturnStatement()
- throws UnableToCompleteException {
+ throws UnableToCompleteException {
setupFruitEnum();
setupVegetableEnum();
addSnippetClassDecl("public static Enum returnAsEnum(int mode) {",
- " if (mode == 0) {",
- " return Fruit.APPLE;",
- " } else {",
- " return Vegetable.CARROT;",
- " }",
- "}");
+ " if (mode == 0) {",
+ " return Fruit.APPLE;",
+ " } else {",
+ " return Vegetable.CARROT;",
+ " }",
+ "}");
optimize("void", "Enum myEnum = returnAsEnum(0);",
- // do a second one, to prevent inlining
- "Enum myOtherEnum = returnAsEnum(1);");
+ // do a second one, to prevent inlining
+ "Enum myOtherEnum = returnAsEnum(1);",
+ "int ord = Fruit.APPLE.ordinal() + Vegetable.CARROT.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
@@ -875,49 +935,49 @@
private void setupFruitEnumWithInstanceField() {
addSnippetClassDecl("public enum Fruit {APPLE(\"a\"), ORANGE(\"b\");",
- " public final String instanceField;",
- " private Fruit(String str) {",
- " instanceField = str;",
- " }",
- "}");
+ " public final String instanceField;",
+ " private Fruit(String str) {",
+ " instanceField = str;",
+ " }",
+ "}");
}
private void setupFruitEnumWithStaticField() {
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;",
- " public static final String staticField = \"STATIC\";",
- "}");
+ " public static final String staticField = \"STATIC\";",
+ "}");
}
private void setupFruitEnumWithStaticMethod() {
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;",
- " public static final int staticMethod() {",
- " int x = 0;",
- " return x;",
- " }",
- "}");
+ " public static final int staticMethod() {",
+ " int x = 0;",
+ " return x;",
+ " }",
+ "}");
}
private void setupFruitEnumWithStaticMethodThatRefsValuesArray() {
// add a little extra logic here, to prevent inlining
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;",
- " public static Fruit forInteger(int value) {",
- " if (value < 0 || value >= 2) {",
- " return ORANGE;",
- " }",
- " return Fruit.values()[value];",
- " }",
- "}");
+ " public static Fruit forInteger(int value) {",
+ " if (value < 0 || value >= 2) {",
+ " return ORANGE;",
+ " }",
+ " return Fruit.values()[value];",
+ " }",
+ "}");
}
private void setupFruitEnumWithStaticMethodThatRefsValuesLength() {
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;",
- " public static Fruit forInteger(int value) {",
- " if (value < 0 || value >= Fruit.values().length) {",
- " return ORANGE;",
- " }",
- " return APPLE;",
- " }",
- "}");
+ " public static Fruit forInteger(int value) {",
+ " if (value < 0 || value >= Fruit.values().length) {",
+ " return ORANGE;",
+ " }",
+ " return APPLE;",
+ " }",
+ "}");
}
private void setupVegetableEnum() {
@@ -926,29 +986,14 @@
private void setupFruitSwitchMethod() {
addSnippetClassDecl("public static String fruitSwitch(Fruit fruit) {",
- " switch(fruit) {",
- " case APPLE: return \"Apple\";",
- " case ORANGE: return \"Orange\";",
- " default: return \"Unknown\";",
- " }",
- "}");
+ " switch(fruit) {",
+ " case APPLE: return \"Apple\";",
+ " case ORANGE: return \"Orange\";",
+ " default: return \"Unknown\";",
+ " }",
+ "}");
}
- /*
- * Always run ImplementCastsAndTypeChecks and EqualityNormalizer, even in cases where we
- * are testing that ordinalization cannot occur, since there may be other
- * enums (such as DummyEnum) which do get ordinalized, and we want to test
- * that all is well regardless.
- */
- private final boolean performCastReplacement = true;
- private final boolean runEqualityNormalizer = true;
-
- // These are enabled as needed for a given test
- private boolean runMakeCallsStatic;
- private boolean runMethodInliner;
- private boolean runMethodCallTightener;
- private boolean runTypeTightener;
-
@Override
protected boolean optimizeMethod(JProgram program, JMethod method) {
/*
@@ -962,32 +1007,25 @@
* These are a subset of the actual optimizers run in JJS.optimizeLoop().
*/
boolean didChange = false;
- AstDumper.maybeDumpAST(program, "EnumOrdinalizerTest_start");
+ program.addEntryMethod(findMainMethod(program));
if (runMakeCallsStatic) {
didChange = MakeCallsStatic.exec(new JJSOptionsImpl(), program).didChange() || didChange;
- AstDumper.maybeDumpAST(program,
- "EnumOrdinalizerTest_after_makeCallsStatic");
}
if (runTypeTightener) {
didChange = TypeTightener.exec(program).didChange() || didChange;
- AstDumper.maybeDumpAST(program,
- "EnumOrdinalizerTest_after_typeTightener");
}
if (runMethodCallTightener) {
didChange = MethodCallTightener.exec(program).didChange() || didChange;
- AstDumper.maybeDumpAST(program,
- "EnumOrdinalizerTest_after_methodCallTightener");
}
if (runMethodInliner) {
didChange = MethodInliner.exec(program).didChange() || didChange;
- AstDumper.maybeDumpAST(program,
- "EnumOrdinalizerTest_after_methodInliner");
+ }
+ if (runPruner) {
+ didChange = Pruner.exec(program, true).didChange() || didChange;
}
didChange = EnumOrdinalizer.exec(program).didChange() || didChange;
- AstDumper.maybeDumpAST(program,
- "EnumOrdinalizerTest_after_EnumOrdinalizer");
/*
* Run these normalizers to sanity check the AST. If there are any
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 3af876d..35a4c42 100644
--- a/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/CompilerMiscRegressionTest.java
@@ -218,6 +218,27 @@
SomeParentParentParent.callSomeParentParentParentM(new SomeSubSubClassInAnotherPackage()));
}
+ enum MyEnum {
+ A,
+ B,
+ C;
+
+ public final static MyEnum[] VALUES = values();
+
+ public int getPriority() {
+ return VALUES.length - ordinal();
+ }
+ }
+
+ /**
+ * Tests that enum ordinalizer does not incorrectly optimize {@code MyEnum}.
+ * <p>
+ * Test for issue 8846:.
+ */
+ public void testMyEnum() {
+ assertEquals(2, MyEnum.B.getPriority());
+ }
+
private static void assertEqualContents(float[] expected, float[] actual) {
assertEquals("Array length mismatch", expected.length, actual.length);