Annotation Processor minor tweaks and clean up

This commit is contained in:
Mumfrey committed 2015-04-27 14:43:06 +01:00
1 parent 04ac3aac18
commit 6fc0894e81
10 files changed
+348 -202

No files matched your search

@@ -59,74 +59,150 @@ import org.spongepowered.tools.obfuscation.IMixinValidator.ValidationPass;
class AnnotatedMixin {
/**
* Convenience class to store information about an
* {@link org.spongepowered.asm.mixin.Shadow}ed member's names
* A name of an element which may have aliases
*/
class ShadowElementName {
private final boolean hasPrefix;
static class AliasedElementName {
private final String prefix;
private final String baseName;
private final String originalName;
/**
* The original name including any original prefix (the "actual" name)
*/
protected final String originalName;
/**
* Aliases declared by the annotation (if any), never null
*/
private final List<String> aliases;
private String obfuscated;
ShadowElementName(Element method, AnnotationMirror shadow) {
boolean hasPrefix = false;
this.prefix = MirrorUtils.<String>getAnnotationValue(shadow, "prefix", "shadow$");
String name = this.originalName = method.getSimpleName().toString();
if (name.startsWith(this.prefix)) {
hasPrefix = true;
name = name.substring(this.prefix.length());
}
this.hasPrefix = hasPrefix;
this.obfuscated = this.baseName = name;
List<AnnotationValue> aliases = MirrorUtils.<List<AnnotationValue>>getAnnotationValue(shadow, "aliases");
public AliasedElementName(Element element, AnnotationMirror annotation) {
this.originalName = element.getSimpleName().toString();
List<AnnotationValue> aliases = MirrorUtils.<List<AnnotationValue>>getAnnotationValue(annotation, "aliases");
this.aliases = MirrorUtils.<String>unfold(aliases);
}
@Override
public String toString() {
return this.baseName;
}
public ShadowElementName setObfuscatedName(String name) {
this.obfuscated = name.substring(name.lastIndexOf('/') + 1);
return this;
}
public String originalName() {
return this.originalName;
}
public String prefix() {
return this.hasPrefix ? this.prefix : "";
}
public String name() {
return this.prefix(this.baseName);
}
public String obfuscated() {
return this.prefix(this.obfuscated);
}
public String prefix(String name) {
return this.hasPrefix ? this.prefix + name : name;
}
/**
* Get whether this member has any aliases defined
*/
public boolean hasAliases() {
return this.aliases.size() > 0;
}
/**
* Get this member's aliases
*/
public List<String> getAliases() {
return this.aliases;
}
/**
* Gets the original name of the member (including prefix)
*/
public String elementName() {
return this.originalName;
}
public String baseName() {
return this.originalName;
}
}
/**
* Convenience class to store information about an
* {@link org.spongepowered.asm.mixin.Shadow}ed member's names
*/
static class ShadowElementName extends AliasedElementName {
/**
* True if the real element is prefixed
*/
private final boolean hasPrefix;
/**
* Expected prefix read from the annotation, this is set even if
* {@link #hasPrefix} is false
*/
private final String prefix;
/**
* The base name without the prefix
*/
private final String baseName;
/**
* Obfuscated name (once determined)
*/
private String obfuscated;
ShadowElementName(Element element, AnnotationMirror shadow) {
super(element, shadow);
this.prefix = MirrorUtils.<String>getAnnotationValue(shadow, "prefix", "shadow$");
boolean hasPrefix = false;
String name = this.originalName;
if (name.startsWith(this.prefix)) {
hasPrefix = true;
name = name.substring(this.prefix.length());
}
this.hasPrefix = hasPrefix;
this.obfuscated = this.baseName = name;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return this.baseName;
}
@Override
public String baseName() {
return this.baseName;
}
/**
* Sets the obfuscated name for this element
*
* @param name New name
* @return fluent interface
*/
public ShadowElementName setObfuscatedName(String name) {
this.obfuscated = name.substring(name.lastIndexOf('/') + 1);
return this;
}
/**
* Get the prefix (if set), does not return the expected prefix
*/
public String prefix() {
return this.hasPrefix ? this.prefix : "";
}
/**
* Get the base name
*/
public String name() {
return this.prefix(this.baseName);
}
/**
* Gets the obfuscated name (including prefix where appropriate
*/
public String obfuscated() {
return this.prefix(this.obfuscated);
}
/**
* Apply the prefix (if any) to the specified string
*
* @param name String to prefix
* @return Prefixed string or original string if no prefix
*/
public String prefix(String name) {
return this.hasPrefix ? this.prefix + name : name;
}
}
/**
@@ -332,7 +408,8 @@ class AnnotatedMixin {
* Register an {@link org.spongepowered.asm.mixin.Overwrite} method
*/
public void registerOverwrite(ExecutableElement method, AnnotationMirror overwrite) {
this.validateOverwriteTarget(method, overwrite);
AliasedElementName name = new AliasedElementName(method, overwrite);
this.validateTargetMethod(method, overwrite, name, "@Overwrite");
if (!this.remap || !this.validateSingleTarget("@Overwrite", method)) {
return;
@@ -363,45 +440,12 @@ class AnnotatedMixin {
this.addMethodMapping(mcpName, obfName, mcpSignature, obfMethod.sig);
}
/**
* Checks whether the specified method exists in all targets and raises
* warnings where appropriate
*/
private void validateOverwriteTarget(ExecutableElement method, AnnotationMirror overwrite) {
String signature = method.asType().toString();
List<String> aliases = MirrorUtils.<String>unfold(MirrorUtils.<List<AnnotationValue>>getAnnotationValue(overwrite, "aliases"));
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
// Find method as-is
MethodHandle targetMethod = target.findMethod(method);
if (targetMethod != null) {
continue;
}
// Check aliases
for (String alias : aliases) {
if ((targetMethod = target.findMethod(alias, signature)) != null) {
break;
}
}
if (targetMethod == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target for @Overwrite method in " + target, method, overwrite);
}
}
}
/**
* Register a {@link org.spongepowered.asm.mixin.Shadow} field
*/
public void registerShadow(VariableElement field, AnnotationMirror shadow, boolean remap) {
ShadowElementName name = new ShadowElementName(field, shadow);
this.validateShadowTarget(field, shadow, name);
this.validateTargetField(field, shadow, name, "@Shadow");
if (!remap || !this.validateSingleTarget("@Shadow", field)) {
return;
@@ -417,43 +461,12 @@ class AnnotatedMixin {
this.addFieldMapping(name.setObfuscatedName(obfField));
}
/**
* Checks whether the specified field exists in all targets and raises
* warnings where appropriate
*/
private void validateShadowTarget(VariableElement field, AnnotationMirror shadow, ShadowElementName name) {
String fieldType = field.asType().toString();
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
// Search for field
FieldHandle targetField = target.findField(field);
if (targetField != null) {
continue;
}
// Try search by alias
for (String alias : name.getAliases()) {
if ((targetField = target.findField(alias, fieldType)) != null) {
break;
}
}
if (targetField == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target field for @Shadow field in " + target, field, shadow);
}
}
}
/**
* Register a {@link org.spongepowered.asm.mixin.Shadow} method
*/
public void registerShadow(ExecutableElement method, AnnotationMirror shadow, boolean remap) {
ShadowElementName name = new ShadowElementName(method, shadow);
this.validateShadowTarget(method, shadow, name);
this.validateTargetMethod(method, shadow, name, "@Shadow");
if (!remap || !this.validateSingleTarget("@Shadow", method)) {
return;
@@ -470,43 +483,6 @@ class AnnotatedMixin {
this.addMethodMapping(name.setObfuscatedName(obfMethod.name), mcpSignature, obfMethod.sig);
}
/**
* Checks whether the specified method exists in all targets and raises
* warnings where appropriate
*/
private void validateShadowTarget(ExecutableElement method, AnnotationMirror shadow, ShadowElementName name) {
String signature = TypeHandle.getElementSignature(method);
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
// Find method as-is
MethodHandle targetMethod = target.findMethod(method);
if (targetMethod != null) {
continue;
}
// Find method without prefix
targetMethod = target.findMethod(name.toString(), signature);
if (targetMethod != null) {
continue;
}
// Check aliases
for (String alias : name.getAliases()) {
if ((targetMethod = target.findMethod(alias, signature)) != null) {
break;
}
}
if (targetMethod == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target method for @Shadow method in " + target, method, shadow);
}
}
}
/**
* Register a {@link org.spongepowered.asm.mixin.injection.Inject} method
*/
@@ -518,7 +494,7 @@ class AnnotatedMixin {
}
if (targetMember.desc != null) {
this.validateReferencedTarget(method, inject, targetMember);
this.validateReferencedTarget(method, inject, targetMember, "@Inject");
}
if (!remap || !this.validateSingleTarget("@Inject", method)) {
@@ -549,25 +525,6 @@ class AnnotatedMixin {
this.mixins.getReferenceMapper().addMapping(this.classRef, originalReference, remappedReference.toString());
}
/**
* Checks whether the referenced method exists in all targets and raises
* warnings where appropriate
*/
private void validateReferencedTarget(ExecutableElement method, AnnotationMirror inject, MemberInfo reference) {
String signature = reference.toDescriptor();
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
MethodHandle targetMethod = target.findMethod(reference.name, signature);
if (targetMethod == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target method for @Inject in " + target, method, inject);
}
}
}
/**
* Register a {@link org.spongepowered.asm.mixin.injection.At} annotation
* and process the references
@@ -645,6 +602,96 @@ class AnnotatedMixin {
this.methodMappings.add(mapping);
}
/**
* Checks whether the specified method exists in all targets and raises
* warnings where appropriate
*/
private void validateTargetMethod(ExecutableElement method, AnnotationMirror annotation, AliasedElementName name, String type) {
String signature = TypeHandle.getElementSignature(method);
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
// Find method as-is
MethodHandle targetMethod = target.findMethod(method);
if (targetMethod != null) {
continue;
}
if (!name.baseName().equals(name.elementName())) {
// Find method without prefix
targetMethod = target.findMethod(name.baseName(), signature);
if (targetMethod != null) {
continue;
}
}
// Check aliases
for (String alias : name.getAliases()) {
if ((targetMethod = target.findMethod(alias, signature)) != null) {
break;
}
}
if (targetMethod == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target for " + type + " method in " + target, method, annotation);
}
}
}
/**
* Checks whether the specified field exists in all targets and raises
* warnings where appropriate
*/
private void validateTargetField(VariableElement field, AnnotationMirror shadow, AliasedElementName name, String type) {
String fieldType = field.asType().toString();
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
// Search for field
FieldHandle targetField = target.findField(field);
if (targetField != null) {
continue;
}
// Try search by alias
List<String> aliases = name.getAliases();
for (String alias : aliases) {
if ((targetField = target.findField(alias, fieldType)) != null) {
break;
}
}
if (targetField == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target for " + type + " field in " + target, field, shadow);
}
}
}
/**
* Checks whether the referenced method exists in all targets and raises
* warnings where appropriate
*/
private void validateReferencedTarget(ExecutableElement method, AnnotationMirror inject, MemberInfo reference, String type) {
String signature = reference.toDescriptor();
for (TypeHandle target : this.targets) {
if (target.isImaginary()) {
continue;
}
MethodHandle targetMethod = target.findMethod(reference.name, signature);
if (targetMethod == null) {
this.mixins.printMessage(Kind.WARNING, "Cannot find target method for " + type + " in " + target, method, inject);
}
}
}
static Map<String, String> getAtArgs(AnnotationMirror at) {
Map<String, String> args = new HashMap<String, String>();
List<AnnotationValue> argv = MirrorUtils.<List<AnnotationValue>>getAnnotationValue(at, "args");
@@ -26,6 +26,7 @@ package org.spongepowered.tools.obfuscation;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
@@ -33,6 +34,7 @@ import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.annotation.processing.Filer;
@@ -54,6 +56,7 @@ import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import org.spongepowered.asm.launch.MixinBootstrap;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.injection.struct.MemberInfo;
import org.spongepowered.asm.mixin.injection.struct.ReferenceMapper;
@@ -144,22 +147,59 @@ class AnnotatedMixins implements Messager {
*/
private boolean initDone;
/**
* Properties file used to specify options when AP options cannot be
* configured via the build script (eg. when using AP with MCP)
*/
private Properties properties;
/**
* Private constructor, get instances using {@link #getMixinsForEnvironment}
*/
private AnnotatedMixins(ProcessingEnvironment processingEnv) {
this.env = this.detectEnvironment(processingEnv);
this.processingEnv = processingEnv;
this.printMessage(Kind.NOTE, "SpongePowered Mixin Annotation Processor v" + MixinBootstrap.VERSION);
this.reobfSrgFileName = processingEnv.getOptions().get("reobfSrgFile");
this.outSrgFileName = processingEnv.getOptions().get("outSrgFile");
this.outRefMapFileName = processingEnv.getOptions().get("outRefMapFile");
this.reobfSrgFileName = this.getOption("reobfSrgFile");
this.outSrgFileName = this.getOption("outSrgFile");
this.outRefMapFileName = this.getOption("outRefMapFile");
this.validators = ImmutableList.<IMixinValidator>of(
new ParentValidator(processingEnv),
new TargetValidator(processingEnv)
new ParentValidator(processingEnv, this),
new TargetValidator(processingEnv, this)
);
}
private String getOption(String option) {
String value = this.processingEnv.getOptions().get(option);
if (value != null) {
return value;
}
return this.getProperties().getProperty(option);
}
public Properties getProperties() {
if (this.properties == null) {
this.properties = new Properties();
try {
Filer filer = this.processingEnv.getFiler();
FileObject propertyFile = filer.getResource(StandardLocation.SOURCE_PATH, "", "mixin.properties");
if (propertyFile != null) {
InputStream inputStream = propertyFile.openInputStream();
this.properties.load(inputStream);
inputStream.close();
}
} catch (Exception ex) {
// ignore
}
}
return this.properties;
}
private CompilerEnvironment detectEnvironment(ProcessingEnvironment processingEnv) {
if (processingEnv.getClass().getName().contains("jdt")) {
@@ -435,10 +475,17 @@ class AnnotatedMixins implements Messager {
}
}
/**
* Called from each AP class to notify the environment that a new pass is
* starting
*/
public void onPassStarted() {
this.mixinsForPass.clear();
}
/**
* Called from each AP when a pass is completed
*/
public void onPassCompleted() {
for (AnnotatedMixin mixin : this.mixinsForPass) {
mixin.runValidators(ValidationPass.LATE, this.validators);
@@ -27,10 +27,20 @@ package org.spongepowered.tools.obfuscation;
import javax.lang.model.element.VariableElement;
/**
* Retrieved from a {@link TypeHandle} when searching for fields
*/
class FieldHandle {
/**
* Actual element, can be null
*/
private final VariableElement element;
/**
* True if this is a raw type element returned against a query for a
* specialised type
*/
private final boolean rawType;
public FieldHandle(VariableElement element) {
@@ -42,10 +52,24 @@ class FieldHandle {
this.rawType = rawType;
}
/**
* Get whether the element is imaginary (inaccessible via mirror)
*/
public boolean isImaginary() {
return this.element == null;
}
/**
* Get the underlying element, may return null if the handle is imaginary
*/
public VariableElement getElement() {
return this.element;
}
/**
* Returns true if the searched type had a type specifier but the returned
* type does not
*/
public boolean isRawType() {
return this.rawType;
}
@@ -106,7 +106,7 @@ public class InjectionObfuscationProcessor extends MixinProcessor {
if (elem.getKind() == ElementKind.METHOD) {
this.mixins.registerInjector((TypeElement)parent, (ExecutableElement)elem, inject);
} else {
this.processingEnv.getMessager().printMessage(Kind.WARNING,
this.mixins.printMessage(Kind.WARNING,
"Found an @" + injectorClass.getSimpleName() + " annotation on an element which is not a method: " + elem.toString());
}
}
@@ -27,14 +27,30 @@ package org.spongepowered.tools.obfuscation;
import javax.lang.model.element.ExecutableElement;
/**
* Retrieved from a {@link TypeHandle} when searching for methods
*/
class MethodHandle {
/**
* Actual element, can be null
*/
private final ExecutableElement element;
public MethodHandle(ExecutableElement element) {
this.element = element;
}
/**
* Get whether the element is imaginary (inaccessible via mirror)
*/
public boolean isImaginary() {
return this.element == null;
}
/**
* Get the underlying element, may return null if the handle is imaginary
*/
public ExecutableElement getElement() {
return this.element;
}
@@ -57,12 +57,11 @@ public abstract class MixinProcessor extends AbstractProcessor {
protected void processMixins(RoundEnvironment roundEnv) {
this.mixins.onPassStarted();
for (Element elem1 : roundEnv.getElementsAnnotatedWith(Mixin.class)) {
if (elem1.getKind() == ElementKind.CLASS) {
this.mixins.registerMixin((TypeElement)elem1);
for (Element elem : roundEnv.getElementsAnnotatedWith(Mixin.class)) {
if (elem.getKind() == ElementKind.CLASS) {
this.mixins.registerMixin((TypeElement)elem);
} else {
this.processingEnv.getMessager().printMessage(Kind.WARNING,
"Found an @Mixin annotation on an element which is not a class: " + elem1.toString());
this.mixins.printMessage(Kind.ERROR, "Found an @Mixin annotation on an element which is not a class", elem);
}
}
}
@@ -26,6 +26,7 @@ package org.spongepowered.tools.obfuscation;
import java.util.Collection;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
@@ -45,6 +46,11 @@ public abstract class MixinValidator implements IMixinValidator {
*/
protected final ProcessingEnvironment processingEnv;
/**
* Messager to use to output errors and warnings
*/
protected final Messager messager;
/**
* Pass to run this validator in
*/
@@ -54,9 +60,12 @@ public abstract class MixinValidator implements IMixinValidator {
* ctor
*
* @param processingEnv Processing environment
* @param messager Messager to use
* @param pass Validation pass being performed
*/
public MixinValidator(ProcessingEnvironment processingEnv, ValidationPass pass) {
public MixinValidator(ProcessingEnvironment processingEnv, Messager messager, ValidationPass pass) {
this.processingEnv = processingEnv;
this.messager = messager;
this.pass = pass;
}
@@ -84,7 +93,7 @@ public abstract class MixinValidator implements IMixinValidator {
* @param element Element to attach the note to
*/
protected final void note(String note, Element element) {
this.processingEnv.getMessager().printMessage(Kind.NOTE, note, element);
this.messager.printMessage(Kind.NOTE, note, element);
}
/**
@@ -94,7 +103,7 @@ public abstract class MixinValidator implements IMixinValidator {
* @param element Element to attach the warning to
*/
protected final void warning(String warning, Element element) {
this.processingEnv.getMessager().printMessage(Kind.WARNING, warning, element);
this.messager.printMessage(Kind.WARNING, warning, element);
}
/**
@@ -104,7 +113,7 @@ public abstract class MixinValidator implements IMixinValidator {
* @param element Element to attach the error to
*/
protected final void error(String error, Element element) {
this.processingEnv.getMessager().printMessage(Kind.ERROR, error, element);
this.messager.printMessage(Kind.ERROR, error, element);
}
protected final Collection<TypeMirror> getMixinsTargeting(TypeMirror targetType) {
@@ -90,7 +90,8 @@ public class TargetObfuscationProcessor extends MixinProcessor {
for (Element elem : roundEnv.getElementsAnnotatedWith(Shadow.class)) {
Element parent = elem.getEnclosingElement();
if (!(parent instanceof TypeElement)) {
throw new IllegalStateException("@Shadow element has unexpected parent with type " + MirrorUtils.getElementType(parent));
this.mixins.printMessage(Kind.ERROR, "Unexpected parent with type " + MirrorUtils.getElementType(parent), elem);
continue;
}
AnnotationMirror shadow = MirrorUtils.getAnnotation(elem, Shadow.class);
@@ -100,8 +101,7 @@ public class TargetObfuscationProcessor extends MixinProcessor {
} else if (elem.getKind() == ElementKind.METHOD) {
this.mixins.registerShadow((TypeElement)parent, (ExecutableElement)elem, shadow);
} else {
this.processingEnv.getMessager().printMessage(Kind.WARNING,
"Found an @Shadow annotation on an element which is not a method or field: " + elem.toString());
this.mixins.printMessage(Kind.ERROR, "Element is not a method or field", elem);
}
}
}
@@ -114,14 +114,14 @@ public class TargetObfuscationProcessor extends MixinProcessor {
for (Element elem : roundEnv.getElementsAnnotatedWith(Overwrite.class)) {
Element parent = elem.getEnclosingElement();
if (!(parent instanceof TypeElement)) {
throw new IllegalStateException("@Overwrite element has unexpected parent with type " + MirrorUtils.getElementType(parent));
this.mixins.printMessage(Kind.ERROR, "Unexpected parent with type " + MirrorUtils.getElementType(parent), elem);
continue;
}
if (elem.getKind() == ElementKind.METHOD) {
this.mixins.registerOverwrite((TypeElement)parent, (ExecutableElement)elem);
} else {
this.processingEnv.getMessager().printMessage(Kind.WARNING,
"Found an @Overwrite annotation on an element which is not a method: " + elem.toString());
this.mixins.printMessage(Kind.ERROR, "Element is not a method", elem);
}
}
}
@@ -26,6 +26,7 @@ package org.spongepowered.tools.obfuscation.validation;
import java.util.Collection;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.ElementKind;
@@ -45,9 +46,10 @@ public class ParentValidator extends MixinValidator {
* ctor
*
* @param processingEnv Processing environment
* @param messager Messager
*/
public ParentValidator(ProcessingEnvironment processingEnv) {
super(processingEnv, ValidationPass.EARLY);
public ParentValidator(ProcessingEnvironment processingEnv, Messager messager) {
super(processingEnv, messager, ValidationPass.EARLY);
}
/* (non-Javadoc)
@@ -26,6 +26,7 @@ package org.spongepowered.tools.obfuscation.validation;
import java.util.Collection;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
@@ -46,9 +47,10 @@ public class TargetValidator extends MixinValidator {
* ctor
*
* @param processingEnv Processing environment
* @param messager Messager
*/
public TargetValidator(ProcessingEnvironment processingEnv) {
super(processingEnv, ValidationPass.LATE);
public TargetValidator(ProcessingEnvironment processingEnv, Messager messager) {
super(processingEnv, messager, ValidationPass.LATE);
}
/* (non-Javadoc)