Skip to content

Commit

Permalink
J2ObjC external annotations: inject ReflectionSupport annotations to …
Browse files Browse the repository at this point in the history
…types.

This AST visitor injects annotations to a J2ObjC AST from an annotated AST.

In particular, this CL implements what is needed to support annotating types with ReflectionSupport:

- Inject annotations to types.
- Annotations with fields of type enum.

The effects of the ReflectionSupport annotation can be tested by checking the presence/absence of the metadata in the generated implementation file.

	Change on 2018/09/05 by antoniocortes <antoniocortes@google.com>

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=211709234
  • Loading branch information
antonio-cortes-perez authored and Antonio Cortes Perez committed Sep 7, 2018
1 parent 3780df2 commit 4f48ff1
Show file tree
Hide file tree
Showing 3 changed files with 144 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,31 @@
import com.google.devtools.j2objc.ast.TypeDeclaration;
import com.google.devtools.j2objc.ast.UnitTreeVisitor;
import com.google.devtools.j2objc.types.GeneratedAnnotationMirror;
import com.google.devtools.j2objc.types.GeneratedAnnotationValue;
import com.google.devtools.j2objc.types.GeneratedElement;
import com.google.devtools.j2objc.types.GeneratedExecutableElement;
import com.google.devtools.j2objc.types.GeneratedTypeElement;
import com.google.devtools.j2objc.types.GeneratedVariableElement;
import com.google.devtools.j2objc.util.ElementUtil;
import com.google.devtools.j2objc.util.ErrorUtil;
import com.google.devtools.j2objc.util.ExternalAnnotations;
import com.google.devtools.j2objc.util.Mappings;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.type.TypeMirror;
import scenelib.annotations.Annotation;
import scenelib.annotations.el.AClass;
import scenelib.annotations.el.AElement;
import scenelib.annotations.el.AMethod;
import scenelib.annotations.el.AScene;
import scenelib.annotations.field.AnnotationFieldType;
import scenelib.annotations.field.EnumAFT;

/** Adds external annotations from an annotated AST to matching declarations in a J2ObjC AST. */
public final class ExternalAnnotationInjector extends UnitTreeVisitor {
Expand Down Expand Up @@ -76,7 +86,7 @@ public boolean visit(MethodDeclaration node) {
Set<Annotation> annotations = new LinkedHashSet<>();
annotations.addAll(annotatedMethod.tlAnnotationsHere);
annotations.addAll(annotatedMethod.returnType.tlAnnotationsHere);
injectAnnotations(node, annotations);
injectAnnotationsToMethod(node, annotations);
}
return false;
}
Expand All @@ -94,6 +104,9 @@ public void endVisit(EnumDeclaration node) {
private boolean visitAbstractTypeDeclaration(AbstractTypeDeclaration node) {
String elementName = elementUtil.getBinaryName(node.getTypeElement());
AClass annotatedElement = annotatedAst.classes.get(elementName);
if (annotatedElement != null && !annotatedElement.tlAnnotationsHere.isEmpty()) {
injectAnnotationsToType(node, annotatedElement.tlAnnotationsHere);
}
annotatedElementStack.addLast(Optional.ofNullable(annotatedElement));
return true;
}
Expand All @@ -102,14 +115,62 @@ private void endVisitAbstractTypeDeclaration() {
annotatedElementStack.removeLast();
}

private void injectAnnotations(MethodDeclaration node, Set<Annotation> annotations) {
private void injectAnnotationsToType(AbstractTypeDeclaration node, Set<Annotation> annotations) {
GeneratedTypeElement generatedElement = GeneratedTypeElement.mutableCopy(node.getTypeElement());
injectAnnotationsToElement(generatedElement, annotations);
node.setTypeElement(generatedElement);
}

private void injectAnnotationsToMethod(MethodDeclaration node, Set<Annotation> annotations) {
ExecutableElement element = node.getExecutableElement();
GeneratedExecutableElement generatedElement =
GeneratedExecutableElement.mutableCopy(nameTable.getMethodSelector(element), element);
for (Annotation externalAnnotation : annotations) {
generatedElement.addAnnotationMirror(
new GeneratedAnnotationMirror(externalAnnotation.def.name));
}
injectAnnotationsToElement(generatedElement, annotations);
node.setExecutableElement(generatedElement);
}

private void injectAnnotationsToElement(GeneratedElement element, Set<Annotation> annotations) {
for (Annotation annotation : annotations) {
element.addAnnotationMirror(generateAnnotationMirror(annotation));
}
}

private GeneratedAnnotationMirror generateAnnotationMirror(Annotation annotation) {
GeneratedAnnotationMirror annotationMirror = new GeneratedAnnotationMirror(annotation.def.name);
for (Map.Entry<String, Object> entry : annotation.fieldValues.entrySet()) {
String fieldName = entry.getKey();
// For our uses cases, the scenelib library encodes the annotation value as a string.
String fieldValue = (String) entry.getValue();
AnnotationFieldType fieldType = annotation.def.fieldTypes.get(fieldName);
AnnotationField field = generateAnnotationField(fieldType, fieldName, fieldValue);
annotationMirror.addElementValue(field.element, field.value);
}
return annotationMirror;
}

private AnnotationField generateAnnotationField(
AnnotationFieldType type, String name, String value) {
AnnotationField field = new AnnotationField();
if (type instanceof EnumAFT) {
int index = value.lastIndexOf('.');
String enumTypeString = value.substring(0, index);
String enumValue = value.substring(index + 1);
TypeMirror enumType = typeUtil.resolveJavaType(enumTypeString).asType();
field.element =
GeneratedExecutableElement.newMethodWithSelector(
name, enumType, /* enclosingElement = */ null);
field.value =
new GeneratedAnnotationValue(
GeneratedVariableElement.newParameter(
enumValue, enumType, /* enclosingElement = */ null));
} else {
ErrorUtil.error("Unsupported field type in external annotation: " + type);
}
return field;
}

private static class AnnotationField {
ExecutableElement element;
AnnotationValue value;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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.devtools.j2objc.types;

import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;

/** Represents annotation values created during translation. */
public class GeneratedAnnotationValue implements AnnotationValue {

private final Object value;

public GeneratedAnnotationValue(Object value) {
this.value = value;
}

@Override
public String toString() {
return value.toString();
}

@Override
public Object getValue() {
return value;
}

@Override
public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
return v.visit(this, p);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -205,4 +205,38 @@ public void testForwardDeclaration() throws IOException {
assertTranslation(translation, "__attribute__((unused)) static void PTest_foo(PTest *self);");
assertTranslatedLines(translation, "- (void)bar {", " PTest_foo(self);", "}");
}*/

private static final String REFLECTION_SUPPORT_ANNOTATION =
"package com.google.j2objc.annotations: "
+ "annotation @ReflectionSupport: "
+ " enum com.google.j2objc.annotations.ReflectionSupport.Level value ";

public void testInjectReflectionSupport_type_keepReflection() throws IOException {
String externalReflectionSupportAnnotations =
REFLECTION_SUPPORT_ANNOTATION
+ "package p: "
+ "class Test: "
+ " @com.google.j2objc.annotations.ReflectionSupport( "
+ " com.google.j2objc.annotations.ReflectionSupport.Level.FULL) ";
options.addExternalAnnotationFileContents(externalReflectionSupportAnnotations);
options.setStripReflection(true);
String source = "package p; public class Test {}";
String translation = translateSourceFile(source, "p.Test", "p/Test.m");
assertTranslation(translation, "__metadata");
}

public void testInjectReflectionSupport_type_stripReflection() throws IOException {
String externalReflectionSupportAnnotations =
REFLECTION_SUPPORT_ANNOTATION
+ "package p: "
+ "class Test: "
+ " @com.google.j2objc.annotations.ReflectionSupport( "
+ " com.google.j2objc.annotations.ReflectionSupport.Level.NATIVE_ONLY) ";
options.addExternalAnnotationFileContents(externalReflectionSupportAnnotations);
options.setStripReflection(false);
String source = "package p; public class Test {}";
String translation = translateSourceFile(source, "p.Test", "p/Test.m");
assertNotInTranslation(translation, "__metadata");
}

}

0 comments on commit 4f48ff1

Please sign in to comment.