blob: d80f7714a6965045413091dce208ac63ff12e089 [file] [log] [blame]
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JDeclarationStatement;
import com.google.gwt.dev.jjs.ast.JFieldRef;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JValueLiteral;
import com.google.gwt.dev.jjs.ast.JVisitor;
import com.google.gwt.dev.jjs.impl.EnumOrdinalizer.Tracker;
/**
* A set of tests for the conditions under which ordinalization is and is not allowed. The
* ordinalization is performed when allowed.
* <p>
* 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.
* <p>
* NOTE: the tests in this test case are very fragile. On one hand EnumOrdinalizer requires other
* passes to perform their cleanups to be effective; on the other some passes (DeadCodeElimination)
* optimize enums in a way that the tests might become ineffective and some artificial constructs
* are used to avoid this scenario. See {@link EnumOrdinalizerTest#setupNotInlineable()}.
*/
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();
EnumOrdinalizer.enableTracker();
EnumOrdinalizer.resetTracker();
// defaults, can be overridden by individual test cases
runTypeTightener = false;
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
protected void tearDown() throws Exception {
super.tearDown();
}
public void testOrdinalizeBasicAssignment()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit apple = Fruit.APPLE;",
"Fruit orange = Fruit.ORANGE;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeNewArrayAndAssignmentLocalRef()
throws UnableToCompleteException {
setupFruitEnum();
Result result =
optimize("void", "Fruit[] fruits = new Fruit[] {Fruit.APPLE, Fruit.ORANGE, Fruit.APPLE};",
"if (fruits[0] == Fruit.APPLE) {",
" fruits[0] = Fruit.ORANGE;",
"}");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeNewArrayOfArrayAndAssignmentLocalRef()
throws UnableToCompleteException {
setupFruitEnum();
Result result =
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;",
"}");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeNewArrayAndAssignmentFieldRef()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("private final Fruit[] fruits = new Fruit[] ",
" {Fruit.APPLE, Fruit.ORANGE, Fruit.APPLE};");
Result result = optimize("void", "EntryPoint ep = new EntryPoint();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizableFinalFieldUninitializedByDefault()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("private final Fruit uninitializedFinalFruit;",
"public EntryPoint() {",
" uninitializedFinalFruit = Fruit.ORANGE;",
"}");
Result result = optimize("void", "EntryPoint ep = new EntryPoint();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeSwitchStatement()
throws UnableToCompleteException {
setupFruitEnum();
setupFruitSwitchMethod();
Result result = optimize("void", "String apple = fruitSwitch(Fruit.APPLE);",
"String orange = fruitSwitch(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeIfStatement()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl(
"public static String fruitIf(Fruit fruit) {",
" if (fruit == Fruit.APPLE) {",
" return \"Apple\";",
" } else if (fruit == Fruit.ORANGE) {",
" return \"Orange\";",
" } else {",
" return \"Unknown\";",
" }",
"}");
Result result = optimize("void", "String apple = fruitIf(Fruit.APPLE);",
"String orange = fruitIf(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeConditional()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void",
"Fruit fruit = (new Integer(1)).toString().isEmpty() ? Fruit.APPLE : Fruit.ORANGE;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeFieldRefOrdinalMethodCall()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "int i = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeVariableRefOrdinalMethodCall()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"int i = fruit.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeUnusedEmptyEnum() throws UnableToCompleteException {
setupEmptyEnum();
Result result = optimize("void", "EmptyEnum myEnum;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$EmptyEnum") ||
!tracker.isVisited("test.EntryPoint$EmptyEnum"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeUnusedEnum() throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit myEnum;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit") ||
!tracker.isVisited("test.EntryPoint$Fruit"));
// This enum is not referenced and it has gone away before ordinalizing, however make
// sure there are no references anyway.
tracker.addOrdinalized("test.EntryPoint$Fruit");
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizeMethodCallExpressionOrdinalFieldRef()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static Fruit getResolvedFruit(Fruit fruit) {",
" 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;",
"}");
Result result = optimize("void", "int i = switchMethodCall(Fruit.APPLE);",
"Fruit fruit = Fruit.ORANGE;",
"int j = switchMethodCall(fruit);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizableStaticFieldRef()
throws UnableToCompleteException {
// this will cause a static field ref in the enum clinit
setupFruitEnumWithStaticField();
Result result = optimize("void",
"String y = Fruit.staticField + Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizableStaticMethod()
throws UnableToCompleteException {
// this will cause a static method enum class
setupFruitEnumWithStaticMethod();
Result result = optimize("void", "int y = Fruit.staticMethod() + Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizableCallingValues()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "int l = Fruit.values().length;",
"int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
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();
Result result = optimize("void", "Fruit[] fruits = Fruit.values();",
"int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testOrdinalizableStaticMethodThatRefsValuesLength()
throws UnableToCompleteException {
// this will cause a static method that references values().length
setupFruitEnumWithStaticMethodThatRefsValuesLength();
Result result = 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"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableInstanceStaticFieldRef()
throws UnableToCompleteException {
// this will cause a static field ref in the enum clinit
setupFruitEnumWithStaticField();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"String y = fruit.staticField;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableInstanceStaticMethod()
throws UnableToCompleteException {
// this will cause a static method enum class
setupFruitEnumWithStaticMethod();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"int y = fruit.staticMethod();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableClassLiteralReference()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Class clazz = Fruit.class;",
"String clazzStr = clazz.toString() + Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableEnumValueOfWithClassLiteralArg()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Object Carrot = Enum.valueOf(Fruit.class, \"APPLE\");",
"String carrot = Carrot.toString() + Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableGetClassMethodCall()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Class clazz = Fruit.APPLE.getClass();",
"String clazzStr = clazz.toString() + Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableExplicitCastToEnumClass()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Object obj = new Object();",
"Fruit fruit = (Fruit) obj;",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableExplicitCastToArrayOfEnumClass()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Enum[] enumArray = new Enum[10];",
"Fruit[] fruitArray = (Fruit[]) enumArray;",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableExplicitCastFromArrayOfEnumClass()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit[] fruitArray = new Fruit[10];",
"Enum[] enumArray = (Enum[]) fruitArray;",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableExplicitCastToArrayOfArrayOfEnumClass()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Enum[][] enumArray = new Enum[10][10];",
"Fruit[][] fruitArray = (Fruit[][]) enumArray;",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableExplicitCastFromArrayOfArrayOfEnumClass()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit[][] fruitArray = new Fruit[10][10];",
"Enum[][] enumArray = (Enum[][]) fruitArray;",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableExplicitCastFromEnumClass()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Enum Carrot = (Enum) Fruit.APPLE;",
"String carrot = Carrot.toString();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableOrdinalMethodRefFromExplicitCastWithBlackListableSubExpression()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "int ord = " +
"((Fruit) Enum.valueOf(Fruit.class,\"APPLE\")).ordinal();",
"int ord2 = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableInstanceFieldRef()
throws UnableToCompleteException {
// this will cause an instance field ref in the enum constructor
setupFruitEnumWithInstanceField();
Result result = optimize("void", "String instanceField = Fruit.APPLE.instanceField;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableInstanceOfEnumExpression()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"if (fruit instanceof Enum) {",
" fruit = Fruit.ORANGE;",
"}");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableInstanceOfEnumTestType()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Object fruitObj = new Object();",
"if (fruitObj instanceof Fruit) {",
" fruitObj = null;",
"}",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableStaticMethodCallValues()
throws UnableToCompleteException {
// make sure values() method call doesn't doesn't get inlined
runMethodInliner = false;
setupFruitEnum();
Result result = optimize("void", "Fruit[] fruits = Fruit.values();",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableJsniFieldRef()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static Fruit instanceFruit;");
addSnippetClassDecl("public static native void jsniMethod() /*-{",
" var x = @test.EntryPoint::instanceFruit",
"}-*/");
Result result = optimize("void", "instanceFruit = Fruit.APPLE;",
"jsniMethod();",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableJsniFieldRefStatic()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void jsniMethod() /*-{",
" var x = @test.EntryPoint.Fruit::APPLE",
"}-*/");
Result result = optimize("void", "jsniMethod();",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableJsniFieldRefClassLiteral()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void jsniMethod() /*-{",
" var x = @test.EntryPoint.Fruit::class",
"}-*/");
Result result = optimize("void", "jsniMethod();",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpAssignment()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void",
"Enum tomato;",
"tomato = Fruit.APPLE;",
"int ord = tomato.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastFieldInitializedWithNullByDefault()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("static private Fruit uninitializedFruitAsNull;");
Result result = optimize("void", "if (uninitializedFruitAsNull != Fruit.APPLE) {",
" uninitializedFruitAsNull = Fruit.ORANGE;",
"}",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpEquals()
throws UnableToCompleteException {
setupFruitAndVegetableEnums();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"Enum carrot = (Enum) Vegetable.CARROT;",
"boolean test = (fruit == carrot);");
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"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpNotEquals()
throws UnableToCompleteException {
setupFruitAndVegetableEnums();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"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"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertTrue(tracker.isVisited("test.EntryPoint$Vegetable"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Vegetable"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpEqualsNull()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static boolean testIsNull(Fruit fruit) {",
" if (fruit == null) {",
" return true;",
" } else {",
" return false;",
" }",
"}");
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpNotEqualsNull()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static boolean testIsNull(Fruit fruit) {",
" if (fruit != null) {",
" return true;",
" } else {",
" return false;",
" }",
"}");
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcat()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"String str = \"A string followed by \" + fruit;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcat2()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"String str = fruit + \" followed by a string\";");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcatAssignment()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit = Fruit.APPLE;",
"String str = \"A string concatenated with: \";",
"str += fruit;");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastDeclarationToNull()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit = null;",
"int ord = fruit == null ? Util.notInlineable(Fruit.APPLE).ordinal() : fruit.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastAssignmentToNull()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Fruit fruit;",
"fruit = null;",
"int ord = fruit == null ? Util.notInlineable(Fruit.APPLE).ordinal() : fruit.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastDeclaration()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Enum tomato = Fruit.APPLE;",
"int ord = Fruit.APPLE.ordinal() + tomato.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastConditional()
throws UnableToCompleteException {
setupFruitAndVegetableEnums();
Result result = optimize("void", "Enum tomato = null;",
"tomato = (true) ? Fruit.APPLE : Vegetable.CARROT;",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal() +",
" Util.notInlineable(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"));
assertTrue(tracker.isOrdinalized("test.EntryPoint$Vegetable"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastOverriddenMethodReturnType()
throws UnableToCompleteException {
// this test depends on the tighteners running
runTypeTightener = true;
runMethodCallTightener = true;
/*
* Create methods with covariant return type, which the MethodTypeTightener
* will optimize to no longer be covariant, so make sure we check original
* overridden method type.
*/
addSnippetClassDecl("public interface EnumInterface {",
" String name();",
"}");
addSnippetClassDecl("public abstract class AbstractClass<T extends EnumInterface> {",
" public abstract T getEnumClass();",
"}");
addSnippetClassDecl("public class CustomClass1 extends AbstractClass<EnumClass1> {",
" public EnumClass1 getEnumClass() { return EnumClass1.CONST1; }",
"}");
addSnippetClassDecl("public class CustomClass2 extends AbstractClass<EnumClass2> {",
" public EnumClass2 getEnumClass() { return EnumClass2.CONST2; }",
"}");
addSnippetClassDecl("public enum EnumClass1 implements EnumInterface {",
" CONST1;",
"}");
addSnippetClassDecl("public enum EnumClass2 implements EnumInterface {",
" CONST2;",
"}");
addSnippetClassDecl("public static void testEnumClass(AbstractClass abstractClass) {",
" EnumInterface enumClass = abstractClass.getEnumClass();",
"}");
Result result = optimize("void", "EntryPoint ep = new EntryPoint();",
"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"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$EnumClass1"));
assertTrue(tracker.isVisited("test.EntryPoint$EnumClass2"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$EnumClass2"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastMethodCallArgs()
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;",
"}");
Result result = optimize("void", "String stringApple = getEnumString(Fruit.APPLE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastMethodCallArgsNewArray()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static String getEnumString(Enum[] myEnumArray) {",
" String retString = \"\";",
" for (Enum myEnum : myEnumArray) {",
" retString += myEnum.name();",
" }",
" return retString;",
"}");
Result result = optimize("void",
"String stringFruits = getEnumString(new Enum[] {Fruit.APPLE, Fruit.ORANGE});");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastMethodCallVarArgs()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static String getEnumString(Enum...myEnumArray) {",
" String retString = \"\";",
" for (Enum myEnum : myEnumArray) {",
" retString += myEnum.name();",
" }",
" return retString;",
"}");
Result result = optimize("void",
"String stringFruits = getEnumString(Fruit.APPLE, Fruit.ORANGE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastNewArrayElements()
throws UnableToCompleteException {
setupFruitEnum();
Result result = optimize("void", "Enum[] enums = new Enum[] {Fruit.APPLE, Fruit.ORANGE};");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastNewArrayArrayElements()
throws UnableToCompleteException {
setupFruitEnum();
Result result = 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"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableJsniMethodBodyParams()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void passEnumToJsniMethod(Enum myEnum) /*-{",
" myEnum == null; }-*/");
Result result = optimize("void", "passEnumToJsniMethod(Fruit.APPLE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastJsniMethodBodyParams()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void passEnumToJsniMethod(Fruit myEnum) /*-{",
" myEnum == null; }-*/;");
Result result = optimize("void", "passEnumToJsniMethod(Fruit.APPLE);");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableJsniMethodBodyCall()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static native void consumeFruitViaJsni() /*-{",
" var myJso = @test.EntryPoint::calledFromJsni(*)();",
"}-*/;",
"public static Fruit calledFromJsni() {",
" return Fruit.APPLE;",
"}");
Result result = optimize("void", "consumeFruitViaJsni();",
"int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastJsniMethodRefParams()
throws UnableToCompleteException {
setupFruitEnum();
setupFruitSwitchMethod();
addSnippetClassDecl("public static native void fruitSwitchViaJsni() /*-{",
" var myJso;",
" var result = @test.EntryPoint::fruitSwitch(Ltest/EntryPoint$Fruit;)(myJso);",
"}-*/");
Result result = optimize("void", "fruitSwitchViaJsni();",
"int ord = Util.notInlineable(Fruit.APPLE).ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastJsniMethodRefReturnType()
throws UnableToCompleteException {
setupFruitEnum();
addSnippetClassDecl("public static Fruit returnSomeFruit() {",
" return Fruit.APPLE;",
"}");
addSnippetClassDecl("public static native void jsniMethodRefWithEnumReturn() /*-{",
" var result = @test.EntryPoint::returnSomeFruit()();",
"}-*/");
Result result = optimize("void", "jsniMethodRefWithEnumReturn();",
"int ord = Fruit.APPLE.ordinal();");
EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker();
assertTrue(tracker.isVisited("test.EntryPoint$Fruit"));
assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
public void testNotOrdinalizableImplicitUpcastReturnStatement()
throws UnableToCompleteException {
setupFruitAndVegetableEnums();
addSnippetClassDecl("public static Enum returnAsEnum(int mode) {",
" if (mode == 0) {",
" return Fruit.APPLE;",
" } else {",
" return Vegetable.CARROT;",
" }",
"}");
Result result = optimize("void", "Enum myEnum = returnAsEnum(0);",
// do a second one, to prevent inlining
"Enum myOtherEnum = returnAsEnum(1);",
"int ord = myEnum.ordinal() + myOtherEnum.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"));
assertAllEnumOrdinalizedReferencesReplaced(result.getOptimizedProgram(), tracker);
}
private void setupEmptyEnum() {
addSnippetClassDecl("public enum EmptyEnum {}");
}
private void setupFruitEnum() {
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE}");
setupNotInlineable("Fruit");
}
private void setupFruitEnumWithInstanceField() {
addSnippetClassDecl("public enum Fruit {APPLE(\"a\"), ORANGE(\"b\");",
" public final String instanceField;",
" private Fruit(String str) {",
" instanceField = str;",
" }",
"}");
setupNotInlineable("Fruit");
}
private void setupFruitEnumWithStaticField() {
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;",
" public static String staticField = \"STATIC\";",
"}");
setupNotInlineable("Fruit");
}
private void setupFruitEnumWithStaticMethod() {
addSnippetImport("javaemul.internal.annotations.DoNotInline");
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;",
" @DoNotInline",
" public static final int staticMethod() {",
" int x = 0;",
" return x;",
" }",
"}");
setupNotInlineable("Fruit");
}
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];",
" }",
"}");
}
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;",
" }",
"}");
setupNotInlineable("Fruit");
}
private void setupVegetableEnum() {
addSnippetClassDecl("public enum Vegetable {CARROT, SPINACH}");
setupNotInlineable("Vegetable");
}
private void setupFruitAndVegetableEnums() {
addSnippetClassDecl("public enum Fruit {APPLE, ORANGE}");
addSnippetClassDecl("public enum Vegetable {CARROT, SPINACH}");
setupNotInlineable("Fruit", "Vegetable");
}
private void setupFruitSwitchMethod() {
addSnippetClassDecl("public static String fruitSwitch(Fruit fruit) {",
" switch(fruit) {",
" case APPLE: return \"Apple\";",
" case ORANGE: return \"Orange\";",
" default: return \"Unknown\";",
" }",
"}");
}
private void setupNotInlineable(String... classes) {
addSnippetClassDecl("public static class Util {");
for (String clazz : classes) {
addSnippetClassDecl(
" public static " + clazz + " notInlineable(" + clazz + " obj) {",
" if (new Integer(1).toString().isEmpty()) return obj;",
" return obj;",
" }");
}
addSnippetClassDecl("}");
}
@Override
protected boolean doOptimizeMethod(TreeLogger logger, JProgram program, JMethod method) {
/*
* EnumOrdinalizer depends MakeCallsStatic and MethodInliner running before
* it runs, since they cleanup the internal structure of an enum class to
* inline instance methods like $init.
*
* TypeTightener and methodCallTightener are necessary to test some cases
* involving implicit casts on overridden method call return types.
*
* These are a subset of the actual optimizers run in JJS.optimizeLoop().
*/
boolean didChange = false;
program.addEntryMethod(findMainMethod(program));
OptimizerContext optimizerContext = new FullOptimizerContext(program);
if (runMakeCallsStatic) {
didChange = MakeCallsStatic.exec(program, false, optimizerContext).didChange() || didChange;
}
if (runTypeTightener) {
didChange = TypeTightener.exec(program, optimizerContext).didChange() || didChange;
}
if (runMethodCallTightener) {
didChange = MethodCallTightener.exec(program, optimizerContext).didChange() || didChange;
}
if (runMethodInliner) {
didChange = MethodInliner.exec(program, optimizerContext).didChange() || didChange;
}
if (runPruner) {
didChange = Pruner.exec(program, true, optimizerContext).didChange() || didChange;
}
didChange = EnumOrdinalizer.exec(program, optimizerContext).didChange() || didChange;
/*
* Run these normalizers to sanity check the AST. If there are any
* dangling type substitutions, the ImplementCastsAndTypeChecks will generally find it.
* If there are any introduced binary ops between an int and a null, the
* EqualityNormalizer will find it.
*/
if (performCastReplacement) {
ComputeCastabilityInformation.exec(program, false);
ImplementCastsAndTypeChecks.exec(program, false);
}
if (runEqualityNormalizer) {
EqualityNormalizer.exec(program);
}
return didChange;
}
private void assertAllEnumOrdinalizedReferencesReplaced(JProgram program, final Tracker tracker) {
new JVisitor() {
@Override
public void endVisit(JFieldRef x, Context ctx) {
assertTrue(x.getField() + " was not replaced everywhere",
ctx.isLvalue() || !tracker.isOrdinalized(x.getEnclosingType().getName()));
}
@Override
public void endVisit(JDeclarationStatement x, Context ctx) {
assertTrue(x.getVariableRef().getTarget() + " was not replaced everywhere",
x.getInitializer() instanceof JValueLiteral ||
!(x.getVariableRef() instanceof JFieldRef) ||
!tracker.isOrdinalized(
((JFieldRef) x.getVariableRef()).getField().getEnclosingType().getName()));
}
}.accept(program);
}
}