Rolling back Enum Ordinalization Optimization, some issues have been identified. Review at http://gwt-code-reviews.appspot.com/1064801 Review by: zundel@google.com git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9169 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java index 2ebb59c..53f4d53 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java +++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -64,7 +64,6 @@ import com.google.gwt.dev.jjs.impl.ArrayNormalizer; import com.google.gwt.dev.jjs.impl.AssertionNormalizer; import com.google.gwt.dev.jjs.impl.AssertionRemover; -import com.google.gwt.dev.jjs.impl.AstDumper; import com.google.gwt.dev.jjs.impl.BuildTypeMap; import com.google.gwt.dev.jjs.impl.CastNormalizer; import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer; @@ -72,7 +71,6 @@ import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder; import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer; import com.google.gwt.dev.jjs.impl.DeadCodeElimination; -import com.google.gwt.dev.jjs.impl.EnumOrdinalizer; import com.google.gwt.dev.jjs.impl.EqualityNormalizer; import com.google.gwt.dev.jjs.impl.Finalizer; import com.google.gwt.dev.jjs.impl.FixAssignmentToUnbox; @@ -99,6 +97,7 @@ import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs; import com.google.gwt.dev.jjs.impl.ResolveRebinds; import com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer; +import com.google.gwt.dev.jjs.impl.SourceGenerationVisitor; import com.google.gwt.dev.jjs.impl.TypeLinker; import com.google.gwt.dev.jjs.impl.TypeMap; import com.google.gwt.dev.jjs.impl.TypeTightener; @@ -124,9 +123,11 @@ import com.google.gwt.dev.js.ast.JsBlock; import com.google.gwt.dev.js.ast.JsName; import com.google.gwt.dev.js.ast.JsProgram; +import com.google.gwt.dev.util.AbstractTextOutput; import com.google.gwt.dev.util.DefaultTextOutput; import com.google.gwt.dev.util.Empty; import com.google.gwt.dev.util.Memory; +import com.google.gwt.dev.util.TextOutput; import com.google.gwt.dev.util.Util; import com.google.gwt.dev.util.arg.OptionOptimize; import com.google.gwt.dev.util.collect.Lists; @@ -144,8 +145,10 @@ import org.xml.sax.SAXException; import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.io.PrintWriter; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Collection; @@ -566,8 +569,8 @@ allTypeDeclarations = null; Memory.maybeDumpMemory("AstOnly"); - AstDumper.maybeDumpAST(jprogram); - + maybeDumpAST(jprogram); + // See if we should run the EnumNameObfuscator if (module != null) { ConfigurationProperty enumNameObfuscationProp = (ConfigurationProperty) module.getProperties().find( @@ -613,7 +616,7 @@ /* * 4) Possibly optimize some. * - * Don't optimize early if this is a draft compile, or if there's only + * Don't optimizing early if this is a draft compile, or if there's only * one permutation. */ if (options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT @@ -700,7 +703,7 @@ optimizeEvent.end(); throw new InterruptedException(); } - AstDumper.maybeDumpAST(jprogram); + maybeDumpAST(jprogram); OptimizerStats stats = optimizeLoop("Pass " + counter, jprogram, options.isAggressivelyOptimize()); allOptimizerStats.add(stats); @@ -817,13 +820,6 @@ // remove same parameters value stats.add(SameParameterValueOptimizer.exec(jprogram).recordVisits( numNodes)); - - /* - * enum ordinalization - * TODO(jbrosenberg): graduate this out of the 'isAggressivelyOptimize' - * block, over time. - */ - stats.add(EnumOrdinalizer.exec(jprogram).recordVisits(numNodes)); } // prove that any types that have been culled from the main tree are @@ -1267,6 +1263,28 @@ return result; } + private static void maybeDumpAST(JProgram jprogram) { + String dumpFile = System.getProperty("gwt.jjs.dumpAst"); + if (dumpFile != null) { + try { + FileOutputStream os = new FileOutputStream(dumpFile, true); + final PrintWriter pw = new PrintWriter(os); + TextOutput out = new AbstractTextOutput(false) { + { + setPrintWriter(pw); + } + }; + SourceGenerationVisitor v = new SourceGenerationVisitor(out); + v.accept(jprogram); + pw.flush(); + pw.close(); + } catch (IOException e) { + System.out.println("Could not dump AST"); + e.printStackTrace(); + } + } + } + /** * Open an emitted artifact and gunzip its contents. */
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java index 9c0f5b5..a5eac08 100644 --- a/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java +++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JProgram.java
@@ -77,7 +77,6 @@ "java.util.Iterator", "java.lang.AssertionError", "java.lang.Boolean", "java.lang.Byte", "java.lang.Character", "java.lang.Short", "java.lang.Integer", "java.lang.Float", "java.lang.Double", - "java.lang.Throwable", "com.google.gwt.core.client.GWT", "com.google.gwt.core.client.JavaScriptObject", "com.google.gwt.lang.ClassLiteralHolder", @@ -781,10 +780,6 @@ return returnMap; } - public String getClassLiteralName(JType type) { - return type.getJavahSignatureName() + "_classLit"; - } - public CorrelationFactory getCorrelator() { return correlator; } @@ -880,8 +875,8 @@ JClassLiteral.computeClassObjectAllocation(this,info, type); // Create a field in the class literal holder to hold the object. - JField field = new JField(info, getClassLiteralName(type), - typeSpecialClassLiteralHolder, getTypeJavaLangClass(), + JField field = new JField(info, type.getJavahSignatureName() + + "_classLit", typeSpecialClassLiteralHolder, getTypeJavaLangClass(), true, Disposition.FINAL); typeSpecialClassLiteralHolder.addField(field);
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java b/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java deleted file mode 100644 index 9897530..0000000 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/AstDumper.java +++ /dev/null
@@ -1,104 +0,0 @@ -/* - * 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.dev.jjs.ast.JProgram; -import com.google.gwt.dev.util.AbstractTextOutput; -import com.google.gwt.dev.util.TextOutput; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; - -/** - * A simple utility to dump a JProgram to a temp file, which can be called - * sequentially during a compilation/optimization run, so intermediate steps - * can be compared. - * - * It uses the system property "gwt.jjs.dumpAst" to determine the name - * (or prefix) of the file to dump the AST to. - * - * TODO(jbrosenberg): Add proper logging and/or exception handling for the - * potential IOException that might occur when writing the file. - */ -public class AstDumper { - - private static int autoVersionNumber = 0; - - /** - * Appends a new version of the AST at the end of the file, each time it's called. - */ - public static void maybeDumpAST(JProgram jprogram) { - maybeDumpAST(jprogram, null, true); - } - - /** - * Writes the AST to the file with a versioned extension, using an - * auto-incrementing version number (starting from 1), each time it's called. - * Any previous contents of the file written to will be overwritten. - */ - public static void maybeDumpAST(JProgram jprogram, boolean autoIncrementVersion) { - if (!autoIncrementVersion) { - maybeDumpAST(jprogram); - } else { - maybeDumpAST(jprogram, autoVersionNumber++); - } - } - - /** - * Writes the AST to the file with the provided version number extension. - * Any previous contents of the file written to will be overwritten. - */ - public static void maybeDumpAST(JProgram jprogram, int versionNumber) { - String fileExtension = "." + versionNumber; - maybeDumpAST(jprogram, fileExtension, false); - } - - /** - * Writes the AST to the file with the provided version string extension. - * Any previous contents of the file written to will be overwritten. - */ - public static void maybeDumpAST(JProgram jprogram, String versionString) { - String fileExtension = "." + versionString; - maybeDumpAST(jprogram, fileExtension, false); - } - - private static void maybeDumpAST(JProgram jprogram, - String fileExtension, boolean append) { - String dumpFile = System.getProperty("gwt.jjs.dumpAst"); - if (dumpFile != null) { - if (fileExtension != null) { - dumpFile += fileExtension; - } - try { - FileOutputStream os = new FileOutputStream(dumpFile, append); - final PrintWriter pw = new PrintWriter(os); - TextOutput out = new AbstractTextOutput(false) { - { - setPrintWriter(pw); - } - }; - SourceGenerationVisitor v = new SourceGenerationVisitor(out); - v.accept(jprogram); - pw.close(); - } catch (IOException e) { - System.out.println("Could not dump AST"); - e.printStackTrace(); - } - } - } -}
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 deleted file mode 100644 index 1247c2f..0000000 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/EnumOrdinalizer.java +++ /dev/null
@@ -1,956 +0,0 @@ -/* - * 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.dev.jjs.ast.Context; -import com.google.gwt.dev.jjs.ast.JArrayType; -import com.google.gwt.dev.jjs.ast.JCastOperation; -import com.google.gwt.dev.jjs.ast.JClassLiteral; -import com.google.gwt.dev.jjs.ast.JClassType; -import com.google.gwt.dev.jjs.ast.JDeclarationStatement; -import com.google.gwt.dev.jjs.ast.JEnumField; -import com.google.gwt.dev.jjs.ast.JEnumType; -import com.google.gwt.dev.jjs.ast.JExpression; -import com.google.gwt.dev.jjs.ast.JField; -import com.google.gwt.dev.jjs.ast.JFieldRef; -import com.google.gwt.dev.jjs.ast.JMethod; -import com.google.gwt.dev.jjs.ast.JMethodBody; -import com.google.gwt.dev.jjs.ast.JMethodCall; -import com.google.gwt.dev.jjs.ast.JModVisitor; -import com.google.gwt.dev.jjs.ast.JPrimitiveType; -import com.google.gwt.dev.jjs.ast.JProgram; -import com.google.gwt.dev.jjs.ast.JReferenceType; -import com.google.gwt.dev.jjs.ast.JStatement; -import com.google.gwt.dev.jjs.ast.JType; -import com.google.gwt.dev.jjs.ast.JVariableRef; -import com.google.gwt.dev.jjs.ast.js.JsniFieldRef; -import com.google.gwt.dev.jjs.ast.js.JsniMethodRef; -import com.google.gwt.dev.util.log.speedtracer.CompilerEventType; -import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger; -import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * This optimizer replaces enum constants with their ordinal value (a simple - * int) when possible. We call this process "ordinalization". - * - * Generally, this can be done for enums that are only ever referred to by - * reference, or by their ordinal value. For the specific set of conditions - * under which ordinalization can proceed, see the notes for the nested class - * {@link EnumOrdinalizer.CannotBeOrdinalAnalyzer} below. - * - * This optimizer modifies enum classes to change their field constants to ints, - * and to remove initialization of those constants in the clinit method. An - * ordinalized enum class will not be removed from the AST by this optimizer, - * but as long as all references to it are replaced (which is one of the - * requirements for ordinalization), then the enum class itself will be pruned - * by subsequent optimizer passes. - * - * Regardless of whether an ordinalized enum class ends up being completely - * pruned away, the AST is expected to be in a coherent and usable state after - * any pass of this optimizer. Thus, this optimizer should be considered to be - * stateless. - * - * The process is broken up into 3 separate passes over the AST, each - * implemented as a separate visitor class. The first visitor, - * {@link EnumOrdinalizer.CannotBeOrdinalAnalyzer} compiles information - * about each enum class in the AST, and looks for reasons not to ordinalize - * each enum. Thus, it prepares a "black-list" of enum classes that cannot be - * ordinalized (and it follows that all enums that don't get entered in the - * black-list, will be allowed to be ordinalized. The set of conditions which - * cause an enum to be black-listed are outlined below. - * - * If there are enum classes that didn't get black-listed remaining, the - * subsequent passes of the optimizer will be invoked. The first, - * {@link EnumOrdinalizer.ReplaceEnumTypesWithInteger}, replaces the type info - * for each field or expression involved with a target enum constant with an - * integer. The final visitor, descriptively named - * {@link EnumOrdinalizer.ReplaceOrdinalFieldAndMethodRefsWithOrdinal}, will do - * the job of replacing the value for references to the Enum ordinal field or - * method of an enum constant that has been ordinalized. - */ -public class EnumOrdinalizer { - public static String NAME = EnumOrdinalizer.class.getSimpleName(); - public static Tracker tracker = null; - - public static OptimizerStats exec(JProgram program) { - Event optimizeEvent = SpeedTracerLogger.start( - CompilerEventType.OPTIMIZE, "optimizer", NAME); - OptimizerStats stats = new EnumOrdinalizer(program).execImpl(); - optimizeEvent.end("didChange", "" + stats.didChange()); - return stats; - } - - public static Tracker getTracker() { - return tracker; - } - - public static void startTracker() { - tracker = new Tracker(); - } - - public static void stopTracker() { - tracker = null; - } - - private final JProgram program; - private final JType classLiteralHolderType; - private final JType nullType; - private final JType javaScriptObjectType; - private final JType enumType; - private final JField enumOrdinalField; - private final JMethod classCreateForEnumMethod; - private final JMethod enumCreateValueOfMapMethod; - private final JMethod enumSuperConstructor; - private final JMethod enumOrdinalMethod; - private final Set<JEnumType> ordinalizationBlackList = new HashSet<JEnumType>(); - private final Map<String, JEnumType> enumsVisited = new HashMap<String, JEnumType>(); - - public EnumOrdinalizer(JProgram program) { - this.program = program; - this.classLiteralHolderType = program.getIndexedType("ClassLiteralHolder"); - this.nullType = program.getTypeNull(); - this.javaScriptObjectType = program.getJavaScriptObject(); - this.enumType = program.getIndexedType("Enum"); - this.enumOrdinalField = program.getIndexedField("Enum.ordinal"); - this.classCreateForEnumMethod = program.getIndexedMethod("Class.createForEnum"); - this.enumCreateValueOfMapMethod = program.getIndexedMethod("Enum.createValueOfMap"); - this.enumOrdinalMethod = program.getIndexedMethod("Enum.ordinal"); - this.enumSuperConstructor = program.getIndexedMethod("Enum.Enum"); - } - - /** - * A visitor which keeps track of the enums which cannot be ordinalized. It - * does this by keeping track of a "black-list" for ordinals which violate the - * conditions for ordinalization, below. - * - * An enum cannot be ordinalized, if it: - * is implicitly upcast. - * is implicitly cast to from a nullType. - * is implicitly cast to or from a javaScriptObject type. - * is explicitly cast to another type (or vice-versa). - * it's class literal is used explicitly. - * has any field referenced, except for: - * one of it's enum constants - * Enum.ordinal - * has any method called, except for: - * ordinal() - * Enum.ordinal() - * Enum() super constructor - * Enum.createValueOfMap() - * - * This visitor extends the ImplicitUpcastAnalyzer, which encapsulates all - * the conditions where implicit upcasting can occur in an AST. The rest of - * the logic for checking ordinalizability is encapsulated in this sub-class. - * - * It also keeps track of all enums encountered, so we can know if we need to - * continue with the other visitors of the optimizer after this visitor runs. - * - * We make special allowances not to check any code statements that appear - * within the ClassLiteralHolder class, which can contain a reference to all - * enum class literals in the program, even after ordinalization occurs. - * - * Also, we ignore visiting the getClass() method for any enum subclass, since - * it will also cause a visit to the enum's class literal, and we don't - * necessarily want to prevent ordinalization in that case. - * - * Special checking is needed to detect a class literal reference that occurs - * within a JSNI method body. We don't get a visit to JClassLiteral in that - * case, so we need to inspect visits to JsniFieldRef for the possibility it - * might be a reference to a class literal. - * - * We also skip any checking in a method call to Enum.createValueOfMap(), - * since this is generated for any enum class initially within the extra - * enumClass$Map class, and this call contains an implicit upcast in the - * method call args (as well as a reference to the static enumClass$VALUES - * field), which we want to ignore. The enumClass$Map class will not get - * pruned as long as the enumClass is not ordinalized, and so we need to - * ignore it's presence in the consideration for whether an enum class is - * ordinalizable. - */ - private class CannotBeOrdinalAnalyzer extends ImplicitUpcastAnalyzer { - - private final Set<String> jsniClassLiteralsVisited = new HashSet<String>(); - - public CannotBeOrdinalAnalyzer(JProgram program) { - super(program); - } - - /* - * After program is visited, post-process remaining tasks from accumulated data. - */ - public void afterVisitor() { - // black-list any Jsni enum ClassLiteralsVisited - for (String classLiteralName : jsniClassLiteralsVisited) { - JEnumType enumFromLiteral = enumsVisited.get(classLiteralName); - if (enumFromLiteral != null) { - addToBlackList(enumFromLiteral); - } - } - } - - @Override - public void endVisit(JCastOperation x, Context ctx) { - // check for explicit cast (check both directions) - blackListIfEnumCast(x.getExpr().getType(), x.getCastType()); - blackListIfEnumCast(x.getCastType(), x.getExpr().getType()); - } - - @Override - public void endVisit(JClassLiteral x, Context ctx) { - /* - * Check for references to an enum's class literal. We need to - * black-list classes in this case, since there could be a call - * to Enum.valueOf(someEnum.class,"name"), etc. - * - * Note: we won't get here for class literals that occur in the - * ClassLiteralHolder class, or within the getClass method of an - * enum class (see comments above). - */ - JEnumType type = getEnumType(x.getRefType()); - if (type != null) { - blackListIfEnum(type); - } - } - - @Override - public void endVisit(JClassType x, Context ctx) { - // keep track of all enum classes visited - JEnumType maybeEnum = x.isEnumOrSubclass(); - if (maybeEnum != null) { - enumsVisited.put(program.getClassLiteralName(maybeEnum), maybeEnum); - } - } - - @Override - public void endVisit(JFieldRef x, Context ctx) { - // don't need to check Enum.ordinal - if (x.getField() == enumOrdinalField) { - return; - } - - if (x.getInstance() != null) { - // check any instance field reference other than ordinal - blackListIfEnumExpression(x.getInstance()); - } else if (x.getField().isStatic()) { - // check static field references - - /* - * Need to exempt static fieldRefs to the special $VALUES array that - * gets generated for all enum classes, if the reference occurs - * within the enum class itself (such as happens in the clinit() or - * values() method for all enums). - */ - if (x.getField().getName().equals("$VALUES") && - this.currentMethod.getEnclosingType() == - x.getField().getEnclosingType()) { - if (getEnumType(x.getField().getEnclosingType()) != null) { - return; - } - } - - /* - * Need to exempt static fieldRefs for enum constants themselves. - * Detect these as final fields, that have the same enum type as - * their enclosing type. - */ - if (x.getField().isFinal() && - (x.getField().getEnclosingType() == - getEnumType(x.getField().getType()))) { - return; - } - - /* - * Check any other refs to static fields of an enum class. This - * includes references to $VALUES that might occur outside of the enum - * class itself. This can occur when a call to the values() method gets - * inlined, etc. Also check here for any user defined static fields. - */ - blackListIfEnum(x.getField().getEnclosingType()); - } - } - - @Override - public void endVisit(JsniFieldRef x, Context ctx) { - /* - * Can't do the same thing as for JFieldRef, - * all JsniFieldRefs are cast to JavaScriptObjects. - * Need to check both the field type and the type of the instance - * or enclosing class referencing the field. - */ - - // check the field type - blackListIfEnum(x.getField().getType()); - - // check the referrer - if (x.getInstance() != null) { - blackListIfEnumExpression(x.getInstance()); - } else { - blackListIfEnum(x.getField().getEnclosingType()); - } - - /* - * need to also check JsniFieldRef's for a possible reference to a - * class literal, since we don't get a visit to JClassLiteral when - * it occurs within Jsni (shouldn't it?). - */ - if (x.getField().getEnclosingType() == classLiteralHolderType) { - // see if it has an initializer with a method call to "createForEnum" - JExpression initializer = x.getField().getInitializer(); - if (initializer instanceof JMethodCall) { - if (((JMethodCall) initializer).getTarget() == classCreateForEnumMethod) { - jsniClassLiteralsVisited.add(x.getField().getName()); - } - } - } - } - - @Override - public void endVisit(JMethodCall x, Context ctx) { - // exempt calls to certain methods on the Enum super class - if (x.getTarget() == enumCreateValueOfMapMethod || - x.getTarget() == enumSuperConstructor || - x.getTarget() == enumOrdinalMethod) { - return; - } - - // any other method on an enum class should cause it to be black-listed - 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()); - } - } - - // defer to ImplicitUpcastAnalyzer to check method call args & params - super.endVisit(x, ctx); - } - - @Override - public void endVisit(JsniMethodRef x, Context ctx) { - // no enum methods are exempted if occur within a JsniMethodRef - 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()); - } - } - - // defer to ImplicitUpcastAnalyzer to check method call args & params - super.endVisit(x, ctx); - } - - @Override - public boolean visit(JClassType x, Context ctx) { - /* - * Don't want to visit the large ClassLiteralHolder class, it doesn't - * contain references to actual usage of enum class literals. It's also - * a time savings to not traverse this class. - */ - if (x == classLiteralHolderType) { - return false; - } - return true; - } - - @Override - public boolean visit(JFieldRef x, Context ctx) { - /* - * If we have a field ref of Enum.ordinal, then we want to allow a - * cast operation from enum subclass to Enum on the instance. Other - * optimizers have a tendency to convert things like: - * - * 'switch(enumObj)' to - * 'switch((Enum)enumObj).ordinal' - * - * We don't want to blacklist enumObj in that case (as would happen - * if the endVisit handler for JCastOperation above would be triggered). - * So we block the visitor from traversing it's instance expression. - */ - if (x.getField() == enumOrdinalField) { - if (x.getInstance() != null && - x.getInstance() instanceof JCastOperation) { - JCastOperation castOp = (JCastOperation) x.getInstance(); - if (getPossiblyUnderlyingType(castOp.getCastType()) == enumType) { - JEnumType fromType = getEnumType(castOp.getExpr().getType()); - if (fromType != null) { - // exempt this cast operation from further checking - return false; - } - } - } - } - return true; - } - - @Override - public boolean visit(JMethod x, Context ctx) { - /* - * Don't want to visit the generated getClass() method on an enum, since - * it contains a reference to an enum's class literal that we don't want - * to consider. Make sure this is not a user overloaded version - * (i.e. check that it has no params). - */ - if (getEnumType(x.getEnclosingType()) != null && - x.getName().equals("getClass") && - (x.getOriginalParamTypes() == null || x.getOriginalParamTypes().size() == 0)) { - return false; - } - - // defer to parent method on ImplicitCastAnalyzer - return super.visit(x, ctx); - } - - @Override - public boolean visit(JMethodCall x, Context ctx) { - /* - * If we have a method call of Enum.ordinal(), then we want to allow a - * cast operation from enum subclass to Enum on the instance. Other - * optimizers have a tendency to convert things like: - * - * 'switch(enumObj.ordinal())' to - * 'switch((Enum)enumObj).ordinal' - * - * We don't want to blacklist enumObj in that case (as would happen - * if the endVisit handler for JCastOperation above would be triggered). - * So we block the visitor from traversing it's instance expression. - */ - if (x.getTarget() == enumOrdinalMethod) { - if (x.getInstance() != null && - x.getInstance() instanceof JCastOperation) { - JCastOperation castOp = (JCastOperation) x.getInstance(); - if (getPossiblyUnderlyingType(castOp.getCastType()) == enumType) { - JEnumType fromType = getEnumType(castOp.getExpr().getType()); - if (fromType != null) { - // exempt this cast operation from further checking - return false; - } - } - } - } - - /* - * skip all calls to Enum.createValueOfMap, since they'd get falsely - * flagged for referencing $VALUES and for implicitly upcasting an - * array of an enum class, in the arg passing. This method is only - * used by the enumClass$Map class that gets generated for every enum - * class. Once ordinalization proceeds, this $Map class should be pruned. - */ - if (x.getTarget() == enumCreateValueOfMapMethod) { - return false; - } - - // ok to visit - return true; - } - - /* - * Override for the method called from ImplicitUpcastAnalyzer, - * which will be called for any implicit upcast. - */ - @Override - protected void processImplicitUpcast(JType fromType, JType destType) { - if (fromType == nullType) { - // handle case where a nullType is cast to an enum - blackListIfEnum(destType); - } else if (fromType == javaScriptObjectType) { - // handle case where a javaScriptObject is cast to an enum - blackListIfEnum(destType); - } else { - blackListIfEnumCast(fromType, destType); - } - } - - private void addToBlackList(JEnumType enumType) { - ordinalizationBlackList.add(enumType); - } - - private void blackListIfEnum(JType maybeEnum) { - JEnumType actualEnum = getEnumType(maybeEnum); - if (actualEnum != null) { - addToBlackList(actualEnum); - } - } - - private void blackListIfEnumCast(JType maybeEnum, JType destType) { - JEnumType actualEnum = getEnumType(maybeEnum); - JEnumType actualDestType = getEnumType(destType); - if (actualEnum != null) { - if (actualDestType != actualEnum) { - addToBlackList(actualEnum); - } - return; - } - - // check JArrayTypes of enums - actualEnum = getEnumTypeFromArrayElementType(maybeEnum); - actualDestType = getEnumTypeFromArrayElementType(destType); - if (actualEnum != null) { - if (actualDestType != actualEnum) { - addToBlackList(actualEnum); - } - } - } - - private void blackListIfEnumExpression(JExpression instance) { - if (instance != null) { - blackListIfEnum(instance.getType()); - } - } - } - - /** - * A visitor which replaces enum types with an integer. - * - * It sub-classes TypeRemapper, which encapsulates all the locations for a - * settable type. The overridden remap() method will be called in each - * instance, and it will test whether the type is a candidate for replacement, - * and if so, return the new type to set (JPrimitiveType.INT). - * - * Any reference to an enum field constant in an expression is replaced - * with integer. - * - * This will also explicitly replace an enum's field constants with its - * ordinal int values, and remove initialization of enum constants and the - * $VALUES array in the clinit method for the enum. - */ - private class ReplaceEnumTypesWithInteger extends TypeRemapper { - - @Override - public boolean visit(JClassType x, Context ctx) { - // don't waste time visiting the large ClassLiteralHolder class - if (x == classLiteralHolderType) { - return false; - } - - // cleanup clinit method for ordinalizable enums - if (canBeOrdinal(x)) { - // method 0 is always the clinit - cleanupClinit(x.getMethods().get(0)); - } - return true; - } - - @Override - public boolean visit(JField x, Context ctx) { - /* - * Replace an enum field constant, with it's integer valued ordinal. - */ - if (x instanceof JEnumField && canBeOrdinal(x.getEnclosingType())) { - int ordinal = ((JEnumField) x).ordinal(); - x.setInitializer(new JDeclarationStatement(x.getSourceInfo(), - new JFieldRef(x.getSourceInfo(), null, x, - x.getEnclosingType()), program.getLiteralInt(ordinal))); - } - return true; - } - - @Override - public boolean visit(JFieldRef x, Context ctx) { - /* - * Replace an enum field ref with it's integer valued ordinal. - */ - JField field = x.getField(); - if (field instanceof JEnumField && canBeOrdinal(field.getEnclosingType())) { - int ordinal = ((JEnumField) field).ordinal(); - ctx.replaceMe(program.getLiteralInt(ordinal)); - } - return true; - } - - /* - * Remap enum types with JPrimitiveType.INT - * This is an override implementation called from TypeRemapper. - */ - @Override - protected JType remap(JType type) { - if (canBeOrdinal(type)) { - return JPrimitiveType.INT; - } else { - return type; - } - } - - private boolean canBeOrdinal(JType type) { - JType uType = getPossiblyUnderlyingType(type); - return uType instanceof JEnumType && - !ordinalizationBlackList.contains(uType); - } - - /* - * Remove initialization of enum constants, and the $VALUES array, in the - * clinit for an ordinalizable enum. - */ - private void cleanupClinit(JMethod method) { - List<JStatement> stmts = ((JMethodBody) method.getBody()).getStatements(); - Iterator<JStatement> it = stmts.iterator(); - // look for statements of the form EnumValueField = ... - while (it.hasNext()) { - JStatement stmt = it.next(); - if (stmt instanceof JDeclarationStatement) { - JVariableRef ref = ((JDeclarationStatement) stmt).getVariableRef(); - if (ref instanceof JFieldRef) { - JFieldRef enumRef = (JFieldRef) ref; - if (enumRef.getField().getEnclosingType() == method.getEnclosingType()) { - // see if LHS is a field ref that corresponds to the class of this method - if (enumRef.getField() instanceof JEnumField) { - it.remove(); - } else if (enumRef.getField().getName().equals("$VALUES")) { - it.remove(); - } - } - } - } - } - } - } - - /** - * A visitor which will replace references to the ordinal field and - * ordinal method refs with the appropropriate ordinal integer value. - * - * Note, this visitor must run after the ReplaceEnumTypesWithInteger visitor, - * since it depends on detecting the locations where the enumOrdinalField or - * enumOrdinalMethod have had their types changed to integer. - */ - private class ReplaceOrdinalFieldAndMethodRefsWithOrdinal extends JModVisitor { - @Override - public boolean visit(JClassType x, Context ctx) { - // don't waste time visiting the large ClassLiteralHolder class - if (x == classLiteralHolderType) { - return false; - } - return true; - } - - @Override - public boolean visit(JFieldRef x, Context ctx) { - /* - * Check field refs that refer to Enum.ordinal, but which have already - * been ordinalized by the previous pass. This is implemented with visit - * instead of endVisit since, in the case of an underlying cast operation, - * we don't want to traverse the instance expression before we replace it. - */ - if (x.getField() == enumOrdinalField) { - if (x.getInstance() != null) { - JType type = x.getInstance().getType(); - if (type == JPrimitiveType.INT) { - /* - * See if this fieldRef was converted to JPrimitiveType.INT, but - * still points to the Enum.ordinal field. If so, replace the field - * ref with the ordinalized int itself. - */ - ctx.replaceMe(x.getInstance()); - } else if (x.getInstance() instanceof JCastOperation) { - /* - * See if this reference to Enum.ordinal is via a cast from an enum - * sub-class, that we've already ordinalized to JPrimitiveType.INT. - * If so, replace the whole cast operation. - * (see JFieldRef visit method in CannotBeOrdinalAnalyzer above). - */ - JCastOperation castOp = (JCastOperation) x.getInstance(); - if (getPossiblyUnderlyingType(castOp.getType()) == enumType) { - if (castOp.getExpr().getType() == JPrimitiveType.INT) { - ctx.replaceMe(castOp.getExpr()); - } - } - } - } - } - return true; - } - - @Override - public boolean visit(JMethodCall x, Context ctx) { - /* - * See if this methodCall was converted to JPrimitiveType.INT, but still - * points to the Enum.ordinal() method. If so, replace the method call with - * the ordinal expression itself. Implement with visit (and not endVisit), - * so we don't traverse the method call itself unnecessarily. - */ - if (x.getTarget() == enumOrdinalMethod) { - if (x.getInstance() != null) { - JType type = x.getInstance().getType(); - if (type == JPrimitiveType.INT) { - /* - * See if this instance was converted to JPrimitiveType.INT, but still - * points to the Enum.ordinal() method. If so, replace the method call - * with the ordinalized int itself. - */ - ctx.replaceMe(x.getInstance()); - } else if (x.getInstance() instanceof JCastOperation) { - /* - * See if this reference to Enum.ordinal() is via a cast from an enum - * sub-class, that we've already ordinalized to JPrimitiveType.INT. - * If so, replace the whole cast operation. - * (see JMethodCall visit method in CannotBeOrdinalAnalyzer above). - */ - JCastOperation castOp = (JCastOperation) x.getInstance(); - if (getPossiblyUnderlyingType(castOp.getType()) == enumType) { - if (castOp.getExpr().getType() == JPrimitiveType.INT) { - ctx.replaceMe(castOp.getExpr()); - } - } - } - } - } - return true; - } - } - - private OptimizerStats execImpl() { - OptimizerStats stats = new OptimizerStats(NAME); - - if (tracker != null) { - tracker.incrementRunCount(); - tracker.maybeDumpAST(program, 0); - } - - // Create black list of enum refs which can't be converted to an ordinal ref - CannotBeOrdinalAnalyzer ordinalAnalyzer = new CannotBeOrdinalAnalyzer(program); - ordinalAnalyzer.accept(program); - ordinalAnalyzer.afterVisitor(); - - if (tracker != null) { - for (JEnumType type : enumsVisited.values()) { - tracker.addVisited(type.getName()); - if (!ordinalizationBlackList.contains(type)) { - tracker.addOrdinalized(type.getName()); - } - } - } - - // Bail if we don't need to do any ordinalization - if (enumsVisited.size() == ordinalizationBlackList.size()) { - return stats; - } - - // Replace enum type refs - ReplaceEnumTypesWithInteger replaceEnums = new ReplaceEnumTypesWithInteger(); - replaceEnums.accept(program); - stats.recordModified(replaceEnums.getNumMods()); - - if (tracker != null) { - tracker.maybeDumpAST(program, 1); - } - - // Replace enum field and method refs - ReplaceOrdinalFieldAndMethodRefsWithOrdinal replaceOrdinalRefs - = new ReplaceOrdinalFieldAndMethodRefsWithOrdinal(); - replaceOrdinalRefs.accept(program); - stats.recordModified(replaceOrdinalRefs.getNumMods()); - - if (tracker != null) { - tracker.maybeDumpAST(program, 2); - } - - return stats; - } - - private JEnumType getEnumType(JType type) { - type = getPossiblyUnderlyingType(type); - if (type instanceof JClassType) { - return ((JClassType) type).isEnumOrSubclass(); - } - return null; - } - - private JEnumType getEnumTypeFromArrayElementType(JType type) { - type = getPossiblyUnderlyingType(type); - if (type instanceof JArrayType) { - type = ((JArrayType) type).getElementType(); - if (type instanceof JArrayType) { - // recurse if multi-dimensional array - return getEnumTypeFromArrayElementType(type); - } - return getEnumType(type); - } - return null; - } - - private JType getPossiblyUnderlyingType(JType type) { - if (type instanceof JReferenceType) { - return ((JReferenceType) type).getUnderlyingType(); - } - return type; - } - - /** - * A simple Tracker class for compiling lists of enum classes processed by - * this optimizer. If enabled, the results can be logged as debug output, and - * the results can be tested after running with a given input. - */ - public static class Tracker { - private final Set<String> allEnumsVisited; - private final Set<String> allEnumsOrdinalized; - private final List<Set<String>> enumsVisitedPerPass; - private final List<Set<String>> enumsOrdinalizedPerPass; - private int runCount = -1; - - // use TreeSets, for nice sorted iteration for output - public Tracker() { - allEnumsVisited = new TreeSet<String>(); - allEnumsOrdinalized = new TreeSet<String>(); - enumsVisitedPerPass = new ArrayList<Set<String>>(); - enumsOrdinalizedPerPass = new ArrayList<Set<String>>(); - - // add entry for initial pass - enumsVisitedPerPass.add(new TreeSet<String>()); - enumsOrdinalizedPerPass.add(new TreeSet<String>()); - } - - public void addOrdinalized(String ordinalized) { - enumsOrdinalizedPerPass.get(runCount).add(ordinalized); - allEnumsOrdinalized.add(ordinalized); - } - - public void addVisited(String visited) { - enumsVisitedPerPass.get(runCount).add(visited); - allEnumsVisited.add(visited); - } - - public int getNumOrdinalized() { - return allEnumsOrdinalized.size(); - } - - public int getNumVisited() { - return allEnumsVisited.size(); - } - - public void incrementRunCount() { - runCount++; - enumsVisitedPerPass.add(new TreeSet<String>()); - enumsOrdinalizedPerPass.add(new TreeSet<String>()); - } - - public boolean isOrdinalized(String className) { - return allEnumsOrdinalized.contains(className); - } - - public boolean isVisited(String className) { - return allEnumsVisited.contains(className); - } - - public void logEnumsNotOrdinalized(TreeLogger logger, TreeLogger.Type logType) { - if (logger != null) { - logger = logger.branch(logType, "Enums Not Ordinalized:"); - for (String enumVisited : allEnumsVisited) { - if (!isOrdinalized(enumVisited)) { - logger.branch(logType, enumVisited); - } - } - } - } - - public void logEnumsOrdinalized(TreeLogger logger, TreeLogger.Type logType) { - if (logger != null) { - logger = logger.branch(logType, "Enums Ordinalized:"); - for (String enumOrdinalized : allEnumsOrdinalized) { - logger.branch(logType, enumOrdinalized); - } - } - } - - public void logEnumsOrdinalizedPerPass(TreeLogger logger, TreeLogger.Type logType) { - if (logger != null) { - if (allEnumsOrdinalized.size() == 0) { - return; - } - logger = logger.branch(logType, "Enums Ordinalized per Optimization Pass:"); - int pass = 0; - for (Set<String> enumsOrdinalized : enumsOrdinalizedPerPass) { - pass++; - if (enumsOrdinalized.size() > 0) { - TreeLogger subLogger = logger.branch(logType, "Pass " + pass + ": " + - enumsOrdinalized.size() + " ordinalized"); - for (String enumOrdinalized : enumsOrdinalized) { - subLogger.branch(logType, enumOrdinalized); - } - } - } - } - } - - public void logEnumsVisitedPerPass(TreeLogger logger, TreeLogger.Type logType) { - if (logger != null) { - if (allEnumsVisited.size() == 0) { - return; - } - logger = logger.branch(logType, "Enums Visited per Optimization Pass:"); - int pass = 0; - for (Set<String> enumsVisited : enumsVisitedPerPass) { - pass++; - if (enumsVisited.size() > 0) { - TreeLogger subLogger = logger.branch(logType, "Pass " + pass + ": " + - enumsVisited.size() + " visited"); - for (String enumVisited : enumsVisited) { - subLogger.branch(logType, enumVisited); - } - } - } - } - } - - public void logResults(TreeLogger logger, TreeLogger.Type logType) { - logger = logResultsSummary(logger, logType); - logEnumsOrdinalized(logger, logType); - logEnumsNotOrdinalized(logger, logType); - } - - public void logResultsDetailed(TreeLogger logger, TreeLogger.Type logType) { - logger = logResultsSummary(logger, logType); - logEnumsOrdinalizedPerPass(logger, logType); - // logEnumsVisitedPerPass(logger, logType); - logEnumsNotOrdinalized(logger, logType); - } - - public TreeLogger logResultsSummary(TreeLogger logger, TreeLogger.Type logType) { - if (logger != null) { - logger = logger.branch(logType, "EnumOrdinalizer Results:"); - logger.branch(logType, "After pass " + (runCount + 1)); - logger.branch(logType, allEnumsOrdinalized.size() + " of " + - allEnumsVisited.size() + " ordinalized"); - return logger; - } - return null; - } - - public void maybeDumpAST(JProgram program, int stage) { - AstDumper.maybeDumpAST(program, NAME + "_" + (runCount + 1) + "_" + stage); - } - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplicitUpcastAnalyzer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplicitUpcastAnalyzer.java deleted file mode 100644 index 4a2ad14..0000000 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplicitUpcastAnalyzer.java +++ /dev/null
@@ -1,191 +0,0 @@ -/* - * 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.dev.jjs.ast.Context; -import com.google.gwt.dev.jjs.ast.JBinaryOperation; -import com.google.gwt.dev.jjs.ast.JBinaryOperator; -import com.google.gwt.dev.jjs.ast.JConditional; -import com.google.gwt.dev.jjs.ast.JDeclarationStatement; -import com.google.gwt.dev.jjs.ast.JExpression; -import com.google.gwt.dev.jjs.ast.JMethod; -import com.google.gwt.dev.jjs.ast.JMethodCall; -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.JReferenceType; -import com.google.gwt.dev.jjs.ast.JReturnStatement; -import com.google.gwt.dev.jjs.ast.JThrowStatement; -import com.google.gwt.dev.jjs.ast.JType; -import com.google.gwt.dev.jjs.ast.JVisitor; -import com.google.gwt.dev.jjs.ast.js.JsniMethodRef; - -import java.util.List; - -/** - * This class will identify instances of an implicit upcast between non-primitive - * types, and call the overridable processImplicitUpcast method. - * - * TODO(jbrosenberg): Consider extending to handle implicit upcasts between primitive types. - * This is not as straightforward as for reference types, because primitives - * can be boxed and unboxed implicitly as well. - */ -public class ImplicitUpcastAnalyzer extends JVisitor { - - protected JMethod currentMethod; - private final JType throwableType; - private final JType javaScriptObjectType; - private final JType nullType; - - public ImplicitUpcastAnalyzer(JProgram program) { - this.throwableType = program.getIndexedType("Throwable"); - this.javaScriptObjectType = program.getJavaScriptObject(); - this.nullType = program.getTypeNull(); - } - - @Override - public void endVisit(JBinaryOperation x, Context ctx) { - if (x.isAssignment()) { - processIfTypesNotEqual(x.getRhs().getType(), x.getLhs().getType()); - } else if (x.getRhs().getType() == nullType) { - processIfTypesNotEqual(nullType, x.getLhs().getType()); - } else if (x.getLhs().getType() == nullType) { - processIfTypesNotEqual(nullType, x.getRhs().getType()); - } else if (x.getOp() == JBinaryOperator.CONCAT || - x.getOp() == JBinaryOperator.EQ || - x.getOp() == JBinaryOperator.NEQ) { - /* - * Since we are not attempting to handle detection of upcasts between - * primitive types, we limit handling here to CONCAT, EQ and NEQ. - * Need to do both directions. - */ - processIfTypesNotEqual(x.getLhs().getType(), x.getRhs().getType()); - processIfTypesNotEqual(x.getRhs().getType(), x.getLhs().getType()); - } - } - - @Override - public void endVisit(JConditional x, Context ctx) { - processIfTypesNotEqual(x.getThenExpr().getType(), x.getType()); - processIfTypesNotEqual(x.getElseExpr().getType(), x.getType()); - } - - @Override - public void endVisit(JDeclarationStatement x, Context ctx) { - if (x.getInitializer() != null) { - processIfTypesNotEqual(x.getInitializer().getType(), - x.getVariableRef().getType()); - } - } - - @Override - public void endVisit(JMethod x, Context ctx) { - // check for upcast in return type as compared to an overridden method - List<JMethod> overrides = x.getOverrides(); - if (overrides != null && overrides.size() > 0) { - // only check the first one, since other ones will be checked when those - // overridden methods are visited, don't want to do redundant work - processIfTypesNotEqual(x.getType(), overrides.get(0).getType()); - } - - if (x.getBody() != null && x.getBody().isNative()) { - /* - * Check if this method has a native (jsni) method body, in which case - * all arguments passed in are implicitly cast to JavaScriptObject - */ - List<JParameter> params = x.getParams(); - for (int i = 0; i < params.size(); i++) { - processIfTypesNotEqual(params.get(i).getType(), javaScriptObjectType); - } - - /* - * Check if this method has a non-void return type, in which case - * it will be implicitly cast from JavaScriptObject - */ - if (x.getType() != JPrimitiveType.VOID) { - processIfTypesNotEqual(javaScriptObjectType, x.getType()); - } - } - } - - @Override - public void endVisit(JMethodCall x, Context ctx) { - // check for upcast in argument passing - List<JExpression> args = x.getArgs(); - List<JParameter> params = x.getTarget().getParams(); - - for (int i = 0; i < args.size(); i++) { - // make sure the param wasn't pruned - if (i < params.size()) { - processIfTypesNotEqual(args.get(i).getType(), params.get(i).getType()); - } - } - } - - @Override - public void endVisit(JsniMethodRef x, Context ctx) { - // the return type of this method ref will be cast to JavaScriptObject - if (x.getTarget().getType() != JPrimitiveType.VOID) { - processIfTypesNotEqual(x.getTarget().getType(), javaScriptObjectType); - } - - // check referenced method's params, which are passed as JavaScriptObjects - List<JParameter> params = x.getTarget().getParams(); - for (int i = 0; i < params.size(); i++) { - processIfTypesNotEqual(javaScriptObjectType, params.get(i).getType()); - } - } - - @Override - public void endVisit(JReturnStatement x, Context ctx) { - if (x.getExpr() != null) { - // check against the current method return type - processIfTypesNotEqual(x.getExpr().getType(), currentMethod.getType()); - } - } - - @Override - public void endVisit(JThrowStatement x, Context ctx) { - // all things thrown are upcast to a Throwable - JType type = x.getExpr().getType(); - if (type instanceof JReferenceType) { - type = ((JReferenceType) type).getUnderlyingType(); - } - processIfTypesNotEqual(type, throwableType); - } - - @Override - public boolean visit(JMethod x, Context ctx) { - // save this, so can use it later for checking JReturnStatement - currentMethod = x; - return true; - } - - /** - * An overriding method will be called for each detected implicit upcast. - * @param fromType - * @param destType - */ - protected void processImplicitUpcast(JType fromType, JType destType) { - // override - } - - private void processIfTypesNotEqual(JType fromType, JType destType) { - if (fromType != destType) { - processImplicitUpcast(fromType, destType); - } - } -}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeRemapper.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeRemapper.java deleted file mode 100644 index d8298d6..0000000 --- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeRemapper.java +++ /dev/null
@@ -1,95 +0,0 @@ -/* - * 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.dev.jjs.ast.Context; -import com.google.gwt.dev.jjs.ast.JBinaryOperation; -import com.google.gwt.dev.jjs.ast.JCastOperation; -import com.google.gwt.dev.jjs.ast.JConditional; -import com.google.gwt.dev.jjs.ast.JConstructor; -import com.google.gwt.dev.jjs.ast.JGwtCreate; -import com.google.gwt.dev.jjs.ast.JMethod; -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.JType; -import com.google.gwt.dev.jjs.ast.JVariable; - -/** - * This class is a visitor which can find all sites where a JType can be updated - * from one type to another, and calls an overridable remap method for each - * instance. An extending class can override the remap and return a new type - * where it deems it necessary, such as to replace the type of all references - * of a class. - */ -public class TypeRemapper extends JModVisitor { - - @Override - public void endVisit(JBinaryOperation x, Context ctx) { - x.setType(remap(x.getType())); - } - - @Override - public void endVisit(JCastOperation x, Context ctx) { - // JCastOperation doesn't have a settable castType method, so need to - // create a new one and do a replacement. - JType remapCastType = remap(x.getCastType()); - if (remapCastType != x.getCastType()) { - JCastOperation newX = new JCastOperation(x.getSourceInfo(), - remapCastType, x.getExpr()); - ctx.replaceMe(newX); - } - } - - @Override - public void endVisit(JConditional x, Context ctx) { - x.setType(remap(x.getType())); - } - - @Override - public void endVisit(JConstructor x, Context ctx) { - x.setType(remap(x.getType())); - } - - @Override - public void endVisit(JGwtCreate x, Context ctx) { - x.setType(remap(x.getType())); - } - - @Override - public void endVisit(JMethod x, Context ctx) { - x.setType(remap(x.getType())); - } - - @Override - public void endVisit(JNewArray x, Context ctx) { - x.setType((JNonNullType) remap(x.getType())); - } - - @Override - public void endVisit(JVariable x, Context ctx) { - x.setType(remap(x.getType())); - } - - /** - * An overriding method will be called for each detected JType element. - * @param type - */ - protected JType remap(JType type) { - // override to possibly return an different type - return type; - } -}
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 133e356..51e913f 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java +++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -64,57 +64,22 @@ return code; } }; - public static final MockJavaResource CAST = new MockJavaResource( - "com.google.gwt.lang.Cast") { - @Override - protected CharSequence getContent() { - StringBuffer code = new StringBuffer(); - code.append("package com.google.gwt.lang;\n"); - code.append("public final class Cast {\n"); - code.append(" public static Object dynamicCast(Object src, int dstId) { return src;}\n"); - code.append(" public static boolean isNull(Object a) { return false;}\n"); - code.append(" public static boolean isNotNull(Object a) { return false;}\n"); - code.append(" public static boolean jsEquals(Object a, Object b) { return false;}\n"); - code.append(" public static boolean jsNotEquals(Object a, Object b) { return false;}\n"); - code.append("}\n"); - return code; - } - }; public static final MockJavaResource CLASS = new MockJavaResource( "java.lang.Class") { @Override protected CharSequence getContent() { - // This has extra code in the createForEnum method, to keep it from being inlined StringBuffer code = new StringBuffer(); code.append("package java.lang;\n"); code.append("import com.google.gwt.core.client.JavaScriptObject;\n"); code.append("public final class Class<T> {\n"); - code.append(" static <T> Class<T> createForArray(String packageName,\n"); - code.append(" String className, String seedName, Class<?> componentType) {\n"); - code.append(" return new Class<T>(); }\n"); - code.append(" static <T> Class<T> createForClass(String packageName,\n"); - code.append(" String className, String seedName, Class<? super T> superclass) {\n"); - code.append(" return new Class<T>(); }\n"); - code.append(" static <T> Class<T> createForEnum(String packageName,\n"); - code.append(" String className, String seedName, Class<? super T> superclass,\n"); - code.append(" JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) {\n"); - code.append(" Class<T> newClass = new Class<T>();\n"); - code.append(" newClass.className = className;\n"); - code.append(" newClass.packageName = packageName;\n"); - code.append(" newClass.seedName = seedName;\n"); - code.append(" return newClass;}\n"); - code.append(" static <T> Class<T> createForInterface(String packageName, String className) {\n"); - code.append(" return new Class<T>(); }\n"); - code.append(" static <T> Class<T> createForPrimitive(String packageName,\n"); - code.append(" String className, String jni) {\n"); - code.append(" return new Class<T>(); }\n"); + code.append(" static <T> Class<T> createForArray(String packageName, String className, String seedName, Class<?> componentType) { return new Class<T>(); }\n"); + code.append(" static <T> Class<T> createForClass(String packageName, String className, String seedName, Class<? super T> superclass) { return new Class<T>(); }\n"); + code.append(" static <T> Class<T> createForEnum(String packageName, String className, String seedName, Class<? super T> superclass, JavaScriptObject enumConstantsFunc, JavaScriptObject enumValueOfFunc) { return new Class<T>(); }\n"); + code.append(" static <T> Class<T> createForInterface(String packageName, String className) { return new Class<T>(); }\n"); + code.append(" static <T> Class<T> createForPrimitive(String packageName, String className, String jni) { return new Class<T>(); }\n"); code.append(" static boolean isClassMetadataEnabled() { return true; }\n"); code.append(" public boolean desiredAssertionStatus() { return true; }\n"); - code.append(" public String getName() { return className; }\n"); - code.append(" public T[] getEnumConstants() { return null; }\n"); - code.append(" private String packageName = null;"); - code.append(" private String className = null;"); - code.append(" private String seedName = null;"); + code.append(" public String getName() { return null; }\n"); code.append("}\n"); return code; } @@ -139,20 +104,9 @@ code.append("import java.io.Serializable;\n"); code.append("import com.google.gwt.core.client.JavaScriptObject;\n"); code.append("public abstract class Enum<E extends Enum<E>> implements Serializable {\n"); - code.append(" public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) { \n"); - code.append(" for (T enumConstant : enumType.getEnumConstants()) {\n"); - code.append(" if (enumConstant.name() != null) {\n"); - code.append(" return enumConstant;}}\n"); - code.append(" return null;}\n"); - code.append(" protected Enum(String name, int ordinal) { \n"); - code.append(" this.name = name;\n"); - code.append(" this.ordinal = ordinal;}\n"); - code.append(" protected static <T extends Enum<T>> JavaScriptObject createValueOfMap(T[] enumConstants) { return null;}\n"); + code.append(" protected Enum(String name, int ordinal) {}\n"); + code.append(" protected static <T extends Enum<T>> JavaScriptObject createValueOfMap(T[] enumConstants) { return null; }\n"); code.append(" protected static <T extends Enum<T>> T valueOf(JavaScriptObject map, String name) { return null; }\n"); - code.append(" private final String name;\n"); - code.append(" private final int ordinal;\n"); - code.append(" public final String name() { return name; }\n"); - code.append(" public final int ordinal() { return ordinal; }\n"); code.append("}\n"); return code; } @@ -286,7 +240,7 @@ // Replace the basic Class and Enum with a compiler-specific one. result.remove(JavaResourceBase.CLASS); result.remove(JavaResourceBase.ENUM); - Collections.addAll(result, ARRAY, CAST, CLASS, CLASSLITERALHOLDER, ENUM, GWT, + Collections.addAll(result, ARRAY, CLASS, CLASSLITERALHOLDER, ENUM, GWT, RUNASYNCCALLBACK); return result.toArray(new MockJavaResource[result.size()]); }
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 deleted file mode 100644 index c4b760e..0000000 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/EnumOrdinalizerTest.java +++ /dev/null
@@ -1,919 +0,0 @@ -/* - * 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.UnableToCompleteException; -import com.google.gwt.dev.jjs.ast.JMethod; -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 complete test of the resulting ordinalization is not performed. However, - * the CastNormalizer 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. - * - * TODO(jbrosenberg): Provide a test to assert that ordinalization has completed - * correctly, by inspecting the AST in detail, specifically for ordinal - * replacements, after the EnumOrdinalizer completes. - */ -public class EnumOrdinalizerTest extends OptimizerTestBase { - @Override - protected void setUp() throws Exception { - super.setUp(); - - // don't need to runDeadCodeElimination, it's after we're done anyway - runDeadCodeElimination = false; - - // defaults, can be overridden by individual test cases - runTypeTightener = false; - runMethodCallTightener = false; - } - - public void testOrdinalizeBasicAssignment() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit apple = Fruit.APPLE;", - "Fruit orange = Fruit.ORANGE;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testOrdinalizeSwitchStatement() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - setupFruitSwitchMethod(); - optimize("void", "String apple = fruitSwitch(Fruit.APPLE);", - "String orange = fruitSwitch(Fruit.ORANGE);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testOrdinalizeIfStatement() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl( - "public static String fruitIf(Fruit fruit) {", - " if (fruit == Fruit.APPLE) {", - " return \"Apple\";", - " } else if (fruit == Fruit.ORANGE) {", - " return \"Orange\";", - " } else {", - " return \"Unknown\";", - " }", - "}"); - optimize("void", "String apple = fruitIf(Fruit.APPLE);", - "String orange = fruitIf(Fruit.ORANGE);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testOrdinalizeConditional() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit = (true) ? Fruit.APPLE : Fruit.ORANGE;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testOrdinalizeFieldRefOrdinalMethodCall() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "int i = Fruit.APPLE.ordinal();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testOrdinalizeVariableRefOrdinalMethodCall() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "int i = fruit.ordinal();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testOrdinalizeMethodCallExpressionOrdinalFieldRef() - throws UnableToCompleteException { - /* - * the switch expression gets transformed to - * 'switch ((Enum) getResolvedFruit(fruit)).ordinal)' - * by the time the EnumOrdinalizer sees it. So this test is testing the - * logic to replace an ordinal field ref expression with the expression - * itself. - */ - EnumOrdinalizer.startTracker(); - - 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;", - "}"); - optimize("void", "int i = switchMethodCall(Fruit.APPLE);", - "Fruit fruit = Fruit.ORANGE;", - "int j = switchMethodCall(fruit);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableClassLiteralReference() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Class clazz = Fruit.class;", - "String clazzStr = clazz.toString();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableEnumValueOfWithClassLiteralArg() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Object Carrot = Enum.valueOf(Fruit.class, \"APPLE\");", - "String carrot = Carrot.toString();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableGetClassMethodCall() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Class clazz = Fruit.APPLE.getClass();", - "String clazzStr = clazz.toString();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableExplicitCastToEnumClass() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Object obj = new Object();", - "Fruit fruit = (Fruit) obj;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableExplicitCastToArrayOfEnumClass() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Enum[] enumArray = new Enum[10];", - "Fruit[] fruitArray = (Fruit[]) enumArray;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableExplicitCastFromArrayOfEnumClass() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit[] fruitArray = new Fruit[10];", - "Enum[] enumArray = (Enum[]) fruitArray;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableExplicitCastToArrayOfArrayOfEnumClass() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Enum[][] enumArray = new Enum[10][10];", - "Fruit[][] fruitArray = (Fruit[][]) enumArray;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableExplicitCastFromArrayOfArrayOfEnumClass() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit[][] fruitArray = new Fruit[10][10];", - "Enum[][] enumArray = (Enum[][]) fruitArray;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableExplicitCastFromEnumClass() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Enum Carrot = (Enum) Fruit.APPLE;", - "String carrot = Carrot.toString();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableInstanceFieldRef() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - // this will cause an instance field ref in the enum constructor - setupFruitEnumWithInstanceField(); - optimize("void"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableStaticFieldRef() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - // this will cause a static field ref in the enum clinit - setupFruitEnumWithStaticField(); - optimize("void"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableStaticMethodCallValues() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - // this ends up calling Fruit.clinit() first (which is a static method call) - setupFruitEnum(); - optimize("void", "Fruit[] fruits = Fruit.values();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableJsniFieldRef() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static Fruit instanceFruit;"); - addSnippetClassDecl("public static native void jsniMethod() /*-{", - " var x = @test.EntryPoint::instanceFruit", - "}-*/"); - optimize("void", "instanceFruit = Fruit.APPLE;", - "jsniMethod();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableJsniFieldRefStatic() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static native void jsniMethod() /*-{", - " var x = @test.EntryPoint.Fruit::APPLE", - "}-*/"); - optimize("void", "jsniMethod();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableJsniFieldRefClassLiteral() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static native void jsniMethod() /*-{", - " var x = @test.EntryPoint.Fruit::class", - "}-*/"); - optimize("void", "jsniMethod();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpAssignment() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Enum tomato;", - "tomato = Fruit.APPLE;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpEquals() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - setupVegetableEnum(); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "Enum carrot = (Enum) Vegetable.CARROT;", - "boolean test = (fruit == carrot);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - assertTrue(tracker.isVisited("test.EntryPoint$Vegetable")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Vegetable")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpNotEquals() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - setupVegetableEnum(); - 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(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - assertTrue(tracker.isVisited("test.EntryPoint$Vegetable")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Vegetable")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpEqualsNull() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static boolean testIsNull(Fruit fruit) {", - " if (fruit == null) {", - " return true;", - " } else {", - " return false;", - " }", - "}"); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpNotEqualsNull() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static boolean testIsNull(Fruit fruit) {", - " if (fruit != null) {", - " return true;", - " } else {", - " return false;", - " }", - "}"); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "boolean isNull = testIsNull(fruit) || testIsNull(Fruit.ORANGE);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcat() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "String str = \"A string followed by \" + fruit;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcat2() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "String str = fruit + \" followed by a string\";"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastBinaryOpStringConcatAssignment() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit = Fruit.APPLE;", - "String str = \"A string concatenated with: \";", - "str += fruit;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastDeclarationToNull() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit = null;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastAssignmentToNull() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Fruit fruit;", - "fruit = null;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastDeclaration() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - optimize("void", "Enum tomato = Fruit.APPLE;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastConditional() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - setupVegetableEnum(); - optimize("void", "Enum tomato = null;", - "tomato = (true) ? Fruit.APPLE : Vegetable.CARROT;"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - assertTrue(tracker.isVisited("test.EntryPoint$Vegetable")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Vegetable")); - } - - public void testNotOrdinalizableImplicitUpcastOverriddenMethodReturnType() - throws UnableToCompleteException { - - // this test depends on the tighteners running - runTypeTightener = true; - runMethodCallTightener = true; - - EnumOrdinalizer.startTracker(); - - /* - * 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();", - "}"); - 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(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$EnumClass1")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$EnumClass1")); - assertTrue(tracker.isVisited("test.EntryPoint$EnumClass2")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$EnumClass2")); - } - - public void testNotOrdinalizableImplicitUpcastMethodCallArgs() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - 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;", - "}"); - optimize("void", "String stringApple = getEnumString(Fruit.APPLE);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastJsniMethodBodyParams() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static native void passEnumToJsniMethod(Fruit myEnum) /*-{", - "}-*/"); - optimize("void", "passEnumToJsniMethod(Fruit.APPLE);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastJsniMethodBodyReturnType() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static native Fruit returnFruitViaJsni() /*-{", - " var myJso;", - " return myJso;", - "}-*/"); - optimize("void", "Fruit fruit = returnFruitViaJsni();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastJsniMethodRefParams() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - setupFruitSwitchMethod(); - addSnippetClassDecl("public static native void fruitSwitchViaJsni() /*-{", - " var myJso;", - " var result = @test.EntryPoint::fruitSwitch(Ltest/EntryPoint$Fruit;)(myJso);", - "}-*/"); - optimize("void", "fruitSwitchViaJsni();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastJsniMethodRefReturnType() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - addSnippetClassDecl("public static Fruit returnSomeFruit() {", - " return Fruit.APPLE;", - "}"); - addSnippetClassDecl("public static native void jsniMethodRefWithEnumReturn() /*-{", - " var result = @test.EntryPoint::returnSomeFruit()();", - "}-*/"); - optimize("void", "jsniMethodRefWithEnumReturn();"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - } - - public void testNotOrdinalizableImplicitUpcastReturnStatement() - throws UnableToCompleteException { - EnumOrdinalizer.startTracker(); - - setupFruitEnum(); - setupVegetableEnum(); - addSnippetClassDecl("public static Enum returnAsEnum(int mode) {", - " 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);"); - - EnumOrdinalizer.Tracker tracker = EnumOrdinalizer.getTracker(); - EnumOrdinalizer.stopTracker(); - - assertTrue(tracker.isVisited("test.EntryPoint$Fruit")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Fruit")); - assertTrue(tracker.isVisited("test.EntryPoint$Vegetable")); - assertFalse(tracker.isOrdinalized("test.EntryPoint$Vegetable")); - } - - private void setupFruitEnum() { - addSnippetClassDecl("public enum Fruit {APPLE, ORANGE}"); - setupExtraDummyEnum(); - } - - private void setupFruitEnumWithInstanceField() { - addSnippetClassDecl("public enum Fruit {APPLE(\"a\"), ORANGE(\"b\");", - " public final String instanceField;", - " private Fruit(String str) {", - " instanceField = str;", - " }", - "}"); - setupExtraDummyEnum(); - } - - private void setupFruitEnumWithStaticField() { - addSnippetClassDecl("public enum Fruit {APPLE, ORANGE;", - " public static final String staticField = \"STATIC\";", - "}"); - setupExtraDummyEnum(); - } - - private void setupVegetableEnum() { - addSnippetClassDecl("public enum Vegetable {CARROT, SPINACH}"); - } - - private void setupExtraDummyEnum() { - /* - * Assure there are at least more 2 enums in the program, so inlining or - * tightening doesn't push a single enum sub-class into the methods of the - * Enum super-class itself (which prevents ordinalization in most cases). - * TODO(jbrosenberg): Make ordinalization work if there's only 1 enum in - * a program. - */ - addSnippetClassDecl("public enum DummyEnum {DUMMY}"); - } - - private void setupFruitSwitchMethod() { - addSnippetClassDecl("public static String fruitSwitch(Fruit fruit) {", - " switch(fruit) {", - " case APPLE: return \"Apple\";", - " case ORANGE: return \"Orange\";", - " default: return \"Unknown\";", - " }", - "}"); - } - - /* - * Always run CastNormalizer 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 runCastNormalizer = true; - private final boolean runEqualityNormalizer = true; - - /* - * 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. - * TODO(jbrosenberg): Update EnumOrdinalizer to be able to succeed - * irrespective of the ordering and interaction with other optimizers. - */ - private final boolean runMakeCallsStatic = true; - private final boolean runMethodInliner = true; - - // these can be enabled where needed - private boolean runMethodCallTightener = false; - private boolean runTypeTightener = false; - - @Override - protected boolean optimizeMethod(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; - AstDumper.maybeDumpAST(program, "EnumOrdinalizerTest_start"); - - if (runMakeCallsStatic) { - didChange = MakeCallsStatic.exec(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"); - } - - didChange = EnumOrdinalizer.exec(program).didChange() || didChange; - AstDumper.maybeDumpAST(program, - "EnumOrdinalizerTest_after_EnumOrdinalizer"); - - /* - * Run these normalizers to sanity check the AST. If there are any - * dangling type substitutions, the CastNormalizer will generally find it. - * If there are any introduced binary ops between an int and a null, the - * EqualityNormalizer will find it. - */ - if (runCastNormalizer) { - CastNormalizer.exec(program, false); - } - if (runEqualityNormalizer) { - EqualityNormalizer.exec(program); - } - - return didChange; - } -}
diff --git a/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java b/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java index 98a4d13..1dd26ca 100644 --- a/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java +++ b/dev/core/test/com/google/gwt/dev/jjs/impl/OptimizerTestBase.java
@@ -34,17 +34,17 @@ protected final class Result { private final String returnType; private final String originalCode; - private final boolean madeChanges; + private final boolean madeChages; private final JProgram optimizedProgram; private final String methodName; public Result(JProgram optimizedProgram, String returnType, - String methodName, String originalCode, boolean madeChanges) { + String methodName, String originalCode, boolean madeChages) { this.optimizedProgram = optimizedProgram; this.returnType = returnType; this.methodName = methodName; this.originalCode = originalCode; - this.madeChanges = madeChanges; + this.madeChages = madeChages; } public void into(String... expected) throws UnableToCompleteException { @@ -77,7 +77,7 @@ } public void noChange() { - assertFalse(madeChanges); + assertFalse(madeChages); } public JMethod findMethod(String methodName) {