Skip to content

Commit

Permalink
Add "simple" enum initialization, for constants that just have a name…
Browse files Browse the repository at this point in the history
… and ordinal.

	Change on 2016/09/28 by tball <tball@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=134544796
  • Loading branch information
tomball committed Sep 28, 2016
1 parent a4ed85b commit 9d2924f
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,50 @@
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;
import com.google.devtools.j2objc.ast.StringLiteral;
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.
*
Expand Down Expand Up @@ -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<EnumConstantDeclaration> constants = node.getEnumConstants();
List<Statement> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<? extends ExecutableElement, ? extends AnnotationValue> entry
: annotation.getElementValues().entrySet()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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];");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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));
}
}

0 comments on commit 9d2924f

Please sign in to comment.