From 9d2924f57f2d0b5638c6e5f3942377dbc693fc1c Mon Sep 17 00:00:00 2001 From: tball Date: Wed, 28 Sep 2016 09:42:37 -0700 Subject: [PATCH] Add "simple" enum initialization, for constants that just have a name and ordinal. Change on 2016/09/28 by tball ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=134544796 --- .../j2objc/translate/EnumRewriter.java | 94 ++++++++++++++++++- .../devtools/j2objc/util/ElementUtil.java | 14 +++ ...ObjectiveCImplementationGeneratorTest.java | 2 +- .../AnonymousClassConverterTest.java | 4 +- .../j2objc/translate/EnumRewriterTest.java | 17 ++++ .../devtools/j2objc/util/ElementUtilTest.java | 30 ++++++ 6 files changed, 153 insertions(+), 8 deletions(-) diff --git a/translator/src/main/java/com/google/devtools/j2objc/translate/EnumRewriter.java b/translator/src/main/java/com/google/devtools/j2objc/translate/EnumRewriter.java index ffc76639ba..11bd2c7c95 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/translate/EnumRewriter.java +++ b/translator/src/main/java/com/google/devtools/j2objc/translate/EnumRewriter.java @@ -22,13 +22,17 @@ import com.google.devtools.j2objc.ast.ConstructorInvocation; import com.google.devtools.j2objc.ast.EnumConstantDeclaration; import com.google.devtools.j2objc.ast.EnumDeclaration; +import com.google.devtools.j2objc.ast.Expression; import com.google.devtools.j2objc.ast.ExpressionStatement; +import com.google.devtools.j2objc.ast.ForStatement; import com.google.devtools.j2objc.ast.FunctionInvocation; +import com.google.devtools.j2objc.ast.InfixExpression; import com.google.devtools.j2objc.ast.MethodDeclaration; import com.google.devtools.j2objc.ast.NativeDeclaration; import com.google.devtools.j2objc.ast.NativeExpression; import com.google.devtools.j2objc.ast.NativeStatement; import com.google.devtools.j2objc.ast.NumberLiteral; +import com.google.devtools.j2objc.ast.PostfixExpression; import com.google.devtools.j2objc.ast.SimpleName; import com.google.devtools.j2objc.ast.SingleVariableDeclaration; import com.google.devtools.j2objc.ast.Statement; @@ -36,24 +40,32 @@ import com.google.devtools.j2objc.ast.SuperConstructorInvocation; import com.google.devtools.j2objc.ast.TreeUtil; import com.google.devtools.j2objc.ast.TreeVisitor; +import com.google.devtools.j2objc.ast.Type; +import com.google.devtools.j2objc.ast.VariableDeclarationExpression; +import com.google.devtools.j2objc.ast.VariableDeclarationFragment; import com.google.devtools.j2objc.ast.VariableDeclarationStatement; import com.google.devtools.j2objc.jdt.BindingConverter; import com.google.devtools.j2objc.types.FunctionBinding; import com.google.devtools.j2objc.types.GeneratedMethodBinding; import com.google.devtools.j2objc.types.GeneratedTypeBinding; import com.google.devtools.j2objc.types.GeneratedVariableBinding; +import com.google.devtools.j2objc.types.GeneratedVariableElement; import com.google.devtools.j2objc.util.BindingUtil; +import com.google.devtools.j2objc.util.ElementUtil; import com.google.devtools.j2objc.util.NameTable; import com.google.devtools.j2objc.util.UnicodeUtils; - +import com.google.j2objc.annotations.ObjectiveCName; +import java.util.ArrayList; +import java.util.List; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import org.eclipse.jdt.core.dom.IMethodBinding; import org.eclipse.jdt.core.dom.ITypeBinding; import org.eclipse.jdt.core.dom.IVariableBinding; import org.eclipse.jdt.core.dom.Modifier; -import java.util.ArrayList; -import java.util.List; - /** * Modifies enum types for Objective C. * @@ -86,10 +98,82 @@ private void addEnumInitialization(EnumDeclaration node) { if (Options.useARC()) { addArcInitialization(node); } else { - addNonArcInitialization(node); + if (isSimpleEnum(node)) { + addSimpleNonArcInitialization(node); + } else { + addNonArcInitialization(node); + } + } + } + + /** + * Returns true if an enum doesn't have custom or renamed constructors, + * vararg constructors or constants with anonymous class extensions. + */ + private boolean isSimpleEnum(EnumDeclaration node) { + TypeElement type = node.getTypeElement(); + for (EnumConstantDeclaration constant : node.getEnumConstants()) { + ExecutableElement method = constant.getExecutableElement(); + if (method.getParameters().size() > 0 || method.isVarArgs()) { + return false; + } + if (ElementUtil.hasAnnotation(method, ObjectiveCName.class)) { + return false; + } + TypeElement valueType = ElementUtil.getDeclaringClass(method); + if (valueType != type) { + return false; + } } + return true; } + private void addSimpleNonArcInitialization(EnumDeclaration node) { + List constants = node.getEnumConstants(); + List stmts = node.getClassInitStatements().subList(0, 0); + stmts.add(new NativeStatement("size_t objSize = class_getInstanceSize(self);")); + stmts.add(new NativeStatement(UnicodeUtils.format( + "size_t allocSize = %s * objSize;", constants.size()))); + stmts.add(new NativeStatement("uintptr_t ptr = (uintptr_t)calloc(allocSize, 1);")); + GeneratedVariableBinding localEnum = + new GeneratedVariableBinding("e", 0, typeEnv.getIdType(), false, false, null, null); + stmts.add(new VariableDeclarationStatement(localEnum, null)); + + StringBuffer sb = new StringBuffer("id names[] = {\n "); + for (EnumConstantDeclaration constant : node.getEnumConstants()) { + sb.append("@\"" + constant.getName() + "\", "); + } + sb.append("\n};"); + stmts.add(new NativeStatement(sb.toString())); + + TypeMirror intType = typeEnv.resolveJavaTypeMirror("int"); + GeneratedVariableElement loopCounterElement = + new GeneratedVariableElement("i", intType, + ElementKind.LOCAL_VARIABLE, TreeUtil.getEnclosingElement(node)); + VariableDeclarationExpression loopCounter = + new VariableDeclarationExpression().setType(Type.newType(loopCounterElement.asType())) + .addFragment(new VariableDeclarationFragment( + loopCounterElement, TreeUtil.newLiteral(0, typeEnv))); + Expression loopTest = new InfixExpression() + .setOperator(InfixExpression.Operator.LESS) + .setTypeMirror(intType) + .addOperand(new SimpleName(loopCounterElement)) + .addOperand(TreeUtil.newLiteral(constants.size(), typeEnv)); + Expression loopUpdater = + new PostfixExpression(loopCounterElement, PostfixExpression.Operator.INCREMENT); + Block loopBody = new Block(); + stmts.add(new ForStatement() + .addInitializer(loopCounter) + .setExpression(loopTest) + .addUpdater(loopUpdater) + .setBody(loopBody)); + String enumClassName = nameTable.getFullName(node.getTypeBinding()); + loopBody.addStatement(new NativeStatement("(" + enumClassName + + "_values_[i] = e = objc_constructInstance(self, (void *)ptr), ptr += objSize);")); + loopBody.addStatement(new NativeStatement(enumClassName + + "_initWithNSString_withInt_(e, names[i], i);")); + } + private void addNonArcInitialization(EnumDeclaration node) { ITypeBinding type = node.getTypeBinding(); int baseTypeCount = 0; diff --git a/translator/src/main/java/com/google/devtools/j2objc/util/ElementUtil.java b/translator/src/main/java/com/google/devtools/j2objc/util/ElementUtil.java index 681f36e64e..859919a916 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/util/ElementUtil.java +++ b/translator/src/main/java/com/google/devtools/j2objc/util/ElementUtil.java @@ -444,6 +444,20 @@ public static boolean isRuntimeAnnotation(AnnotationMirror mirror) { return false; } + public static AnnotationMirror getAnnotation(Element element, Class annotationClass) { + for (AnnotationMirror annotation : element.getAnnotationMirrors()) { + String className = TypeUtil.getQualifiedName(annotation.getAnnotationType()); + if (className.equals(annotationClass.getName())) { + return annotation; + } + } + return null; + } + + public static boolean hasAnnotation(Element element, Class annotationClass) { + return getAnnotation(element, annotationClass) != null; + } + public static Object getAnnotationValue(AnnotationMirror annotation, String name) { for (Entry entry : annotation.getElementValues().entrySet()) { diff --git a/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java b/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java index 87813931c4..c4ffdfc468 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/gen/ObjectiveCImplementationGeneratorTest.java @@ -222,7 +222,7 @@ public void testEnum() throws IOException { "Color", "Color.m"); assertTranslation(translation, "Color *Color_values_[3];"); assertTranslation(translation, "@implementation Color"); - assertTranslation(translation, "Color_initWithNSString_withInt_(e, @\"RED\", 0);"); + assertTranslation(translation, "@\"RED\", @\"WHITE\", @\"BLUE\","); assertTranslation(translation, "for (int i = 0; i < 3; i++) {"); assertTranslation(translation, "Color *e = Color_values_[i];"); } diff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/AnonymousClassConverterTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/AnonymousClassConverterTest.java index 5ee4f13a12..b0d71d9e7e 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/translate/AnonymousClassConverterTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/translate/AnonymousClassConverterTest.java @@ -426,10 +426,10 @@ public void testEnumWithInnerEnum() throws IOException { "OuterValue", "OuterValue.m"); // Verify OuterValue constant initialization. - assertTranslation(impl, "OuterValue_initWithNSString_withInt_(e, @\"VALUE1\", 0);"); + assertTranslation(impl, "OuterValue_initWithNSString_withInt_(e, names[i], i);"); // Verify InnerValue constant initialization. - assertTranslation(impl, "OuterValue_InnerValue_initWithNSString_withInt_(e, @\"VALUE1\", 0);"); + assertTranslation(impl, "OuterValue_InnerValue_initWithNSString_withInt_(e, names[i], i);"); } // Tests a field initialized with an anonymous class and multiple diff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/EnumRewriterTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/EnumRewriterTest.java index 7f745321ee..569e152733 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/translate/EnumRewriterTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/translate/EnumRewriterTest.java @@ -60,6 +60,23 @@ public void testEmptyEnum() throws Exception { assertNotInTranslation(source, "Test_Enum"); } + public void testSimpleEnumAllocationCode() throws Exception { + String translation = translateSourceFile( + "enum Test { A, B, C, D, E }", "Test", "Test.m"); + assertTranslatedLines(translation, + "size_t objSize = class_getInstanceSize(self);", + "size_t allocSize = 5 * objSize;", + "uintptr_t ptr = (uintptr_t)calloc(allocSize, 1);", + "id e;", + "id names[] = {", + "@\"A\", @\"B\", @\"C\", @\"D\", @\"E\",", + "};", + "for (jint i = 0; i < 5; i++) {", + "(Test_values_[i] = e = objc_constructInstance(self, (void *)ptr), ptr += objSize);", + "Test_initWithNSString_withInt_(e, names[i], i);", + "}"); + } + public void testEnumAllocationCode() throws Exception { String translation = translateSourceFile( "enum Test { A, B { public String toString() { return \"foo\"; } }, C}", "Test", "Test.m"); diff --git a/translator/src/test/java/com/google/devtools/j2objc/util/ElementUtilTest.java b/translator/src/test/java/com/google/devtools/j2objc/util/ElementUtilTest.java index aa535755e1..c42d3501ec 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/util/ElementUtilTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/util/ElementUtilTest.java @@ -18,7 +18,10 @@ import com.google.devtools.j2objc.ast.AbstractTypeDeclaration; import com.google.devtools.j2objc.ast.Annotation; import com.google.devtools.j2objc.ast.CompilationUnit; +import com.google.j2objc.annotations.ObjectiveCName; import java.io.IOException; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.TypeElement; /** * UnitTests for the {@link ElementUtil} class. @@ -40,4 +43,31 @@ public void testIsRuntimeAnnotation() throws IOException { annotation = decl.getAnnotations().get(0); assertTrue(ElementUtil.isRuntimeAnnotation(annotation.getAnnotationMirror())); } + + public void testGetAnnotation() throws IOException { + CompilationUnit unit = translateType("Example", + "@com.google.j2objc.annotations.ObjectiveCName(\"E\") class Example {}"); + AbstractTypeDeclaration decl = unit.getTypes().get(0); + TypeElement element = decl.getTypeElement(); + AnnotationMirror annotation = ElementUtil.getAnnotation(element, ObjectiveCName.class); + assertEquals("ObjectiveCName", annotation.getAnnotationType().toString()); + } + + public void testGetAnnotationValue() throws IOException { + CompilationUnit unit = translateType("Example", + "@com.google.j2objc.annotations.ObjectiveCName(\"E\") class Example {}"); + AbstractTypeDeclaration decl = unit.getTypes().get(0); + TypeElement element = decl.getTypeElement(); + AnnotationMirror annotation = ElementUtil.getAnnotation(element, ObjectiveCName.class); + Object value = ElementUtil.getAnnotationValue(annotation, "value"); + assertEquals("E", value); + } + + public void testHasAnnotation() throws IOException { + CompilationUnit unit = translateType("Example", + "@com.google.j2objc.annotations.ObjectiveCName(\"E\") class Example {}"); + AbstractTypeDeclaration decl = unit.getTypes().get(0); + TypeElement element = decl.getTypeElement(); + assertTrue(ElementUtil.hasAnnotation(element, ObjectiveCName.class)); + } }