From 077f726baef67ed5472ae3c3deab87df22323332 Mon Sep 17 00:00:00 2001 From: Fury_Phoenix <64714532+Phoenix-Starlight@users.noreply.github.com> Date: Sat, 9 Dec 2023 19:03:45 -0800 Subject: [PATCH] Fix platform support - Prefer TypeElements over TypeMirrors - Add debug option to log classes that couldn't be loaded - Reorganize exception logging --- annotation-processor/build.gradle | 3 +- .../annotation/ClientMixinValidator.java | 119 +++++++++++++----- .../mixinAp/annotation/MixinProcessor.java | 36 +++--- .../mixinAp/config/MixinConfig.java | 15 +-- 4 files changed, 113 insertions(+), 60 deletions(-) diff --git a/annotation-processor/build.gradle b/annotation-processor/build.gradle index bcb4cfbc..1ae88ed8 100644 --- a/annotation-processor/build.gradle +++ b/annotation-processor/build.gradle @@ -17,7 +17,6 @@ dependencies { compileOnly 'com.google.auto.service:auto-service:1.1.1' implementation "net.fabricmc:sponge-mixin:0.12.5+" - implementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" implementation project(":annotations") } @@ -30,4 +29,4 @@ spotless { removeUnusedImports() } } -version = '1.1.0' +version = '1.1.1' diff --git a/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/ClientMixinValidator.java b/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/ClientMixinValidator.java index c2f77ee7..b36b9f2a 100644 --- a/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/ClientMixinValidator.java +++ b/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/ClientMixinValidator.java @@ -1,7 +1,11 @@ package org.fury_phoenix.mixinAp.annotation; +import com.google.common.base.Throwables; + import java.lang.annotation.Annotation; +import java.lang.invoke.*; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -9,10 +13,10 @@ import java.util.stream.Stream; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.AnnotatedConstruct; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; import javax.tools.Diagnostic; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; @@ -28,24 +32,50 @@ public class ClientMixinValidator { private final ITypeHandleProvider typeHandleProvider; - private final ProcessingEnvironment processingEnv; - private final Messager messager; private final Elements elemUtils; - private final Class markerClass = getMarkerClass(); + private final Types types; - private static final Set markers = Set.of( + private final boolean debug; + + private final Class markerClass = getMarkerClass(markers); + + private final Class> markerEnumClass = getMarkerEnumClass(markerEnums); + + private static final Collection markers = Set.of( "net.fabricmc.api.Environment", "net.minecraftforge.api.distmarker.OnlyIn", "net.neoforged.api.distmarker.OnlyIn"); - public ClientMixinValidator(ProcessingEnvironment env) { + private static final Collection markerEnums = Set.of( + "net.fabricmc.api.EnvType", + "net.minecraftforge.api.distmarker.Dist", + "net.neoforged.api.distmarker.Dist"); + + private static final Collection unannotatedClasses = new HashSet<>(); + + private static final MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + + private final MethodType enumValueAccessorType = MethodType.methodType(markerEnumClass); + + private final MethodHandle enumValueAccessor; + + public ClientMixinValidator(ProcessingEnvironment env) + throws ReflectiveOperationException { typeHandleProvider = AnnotatedMixinsAccessor.getMixinAP(env); - processingEnv = env; + debug = Boolean.valueOf(env.getOptions().get("org.fury_phoenix.mixinAp.validator.debug")); messager = env.getMessager(); elemUtils = env.getElementUtils(); + types = env.getTypeUtils(); + try { enumValueAccessor = getMethod(markerClass); } + catch (ReflectiveOperationException e) { throw e; } + } + + private MethodHandle getMethod(Class clz) + throws ReflectiveOperationException { + return lookup.findVirtual(clz, "value", enumValueAccessorType); } public boolean validateMixin(TypeElement annotatedMixinClass) { @@ -61,44 +91,52 @@ public class ClientMixinValidator { } private boolean targetsClient(List classTargets) { - return classTargets.stream() - .anyMatch(this::targetsClient); + return classTargets.stream().anyMatch(this::targetsClient); } private boolean targetsClient(Object classTarget) { return switch (classTarget) { - case TypeMirror tm -> - isClientMarked(tm); + case TypeElement te -> + isClientMarked(te); + case TypeMirror tm -> { + var el = types.asElement(tm); + yield el != null ? targetsClient(el) : warn("TypeMirror of " + tm); + } // If you're using a dollar sign in class names you are insane - case String s -> - targetsClient(elemUtils.getTypeElement(toSourceString(s.split("\\$")[0])).asType()); + case String s -> { + var te = + elemUtils.getTypeElement(toSourceString(s.split("\\$")[0])); + yield te != null ? targetsClient(te) : warn(s); + } default -> - throw new IllegalArgumentException("Unhandled type: " + classTarget.getClass() + "\n" - + "Stringified contents: " + classTarget.toString()); + throw new IllegalArgumentException("Unhandled type: " + + classTarget.getClass() + "\n" + "Stringified contents: " + + classTarget.toString()); }; } - private boolean isClientMarked(AnnotatedConstruct ac) { - TypeHandle handle = getTypeHandle(ac); - if(handle == null) { - messager.printMessage(Diagnostic.Kind.WARNING, "Class can't be loaded! " + ac); + private boolean isClientMarked(TypeElement te) { + Annotation marker = te.getAnnotation(markerClass); + if(marker == null) { + if(debug && unannotatedClasses.add(te.toString())) { + messager.printMessage(Diagnostic.Kind.WARNING, + "Missing " + markerClass.getCanonicalName() + " on " + te + "!"); + } return false; } - IAnnotationHandle marker = handle.getAnnotation(markerClass); - - if(marker == null) return false; - - String[] markerEnum = marker.getValue("value"); - - if(markerEnum == null) return false; - - String markerEnumValue = markerEnum[1]; - return markerEnumValue.toString().equals("CLIENT"); + try { + Object value = enumValueAccessor.invoke(marker); + return value.toString().equals("CLIENT"); + } catch (Throwable e) { + messager.printMessage(Diagnostic.Kind.ERROR, "Fatal error:" + + Throwables.getStackTraceAsString(e)); + } + return false; } @SuppressWarnings("unchecked") - private static Class getMarkerClass() { - for(var annotation : markers) { + private static Class getMarkerClass(Collection markerSet) { + for(var annotation : markerSet) { try { return (Class)Class.forName(annotation); } catch (ClassNotFoundException e) {} @@ -106,8 +144,19 @@ public class ClientMixinValidator { return null; } + @SuppressWarnings("unchecked") + private static Class> getMarkerEnumClass(Collection enumSet) { + for(var enumClass : enumSet) { + try { + return (Class>)Class.forName(enumClass); + } catch (ClassNotFoundException e) {} + } + return null; + } + private boolean warn(Object o) { - messager.printMessage(Diagnostic.Kind.WARNING, o + " can't be loaded, so it is skipped!"); + messager.printMessage(Diagnostic.Kind.WARNING, + toSourceString(o.toString()) + " can't be loaded, so it is skipped!"); return false; } @@ -117,7 +166,8 @@ public class ClientMixinValidator { annotatedMixinClass.getQualifiedName(), ClientMixinValidator.getTargets( getAnnotationHandle(annotatedMixinClass, Mixin.class) - ).stream().filter(this::targetsClient) + ).stream() + .filter(this::targetsClient) .map(Object::toString) .map(ClientMixinValidator::toSourceString) .collect(Collectors.joining(", ")) @@ -128,7 +178,8 @@ public class ClientMixinValidator { return typeHandleProvider.getTypeHandle(annotatedClass); } - private IAnnotationHandle getAnnotationHandle(Object annotatedClass, Class annotation) { + private IAnnotationHandle + getAnnotationHandle(Object annotatedClass, Class annotation) { return getTypeHandle(annotatedClass).getAnnotation(annotation); } diff --git a/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/MixinProcessor.java b/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/MixinProcessor.java index 4d4b728d..9c584f50 100644 --- a/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/MixinProcessor.java +++ b/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/annotation/MixinProcessor.java @@ -1,6 +1,7 @@ package org.fury_phoenix.mixinAp.annotation; import com.google.auto.service.AutoService; +import com.google.common.base.Throwables; import java.util.List; import java.util.HashMap; @@ -23,7 +24,7 @@ import javax.tools.Diagnostic; import org.fury_phoenix.mixinAp.config.MixinConfig; @SupportedAnnotationTypes({"org.spongepowered.asm.mixin.Mixin", "org.embeddedt.modernfix.annotation.ClientOnlyMixin"}) -@SupportedOptions({"rootProject.name", "project.name"}) +@SupportedOptions({"rootProject.name", "project.name", "org.fury_phoenix.mixinAp.validator.debug"}) @SupportedSourceVersion(SourceVersion.RELEASE_17) @AutoService(Processor.class) public class MixinProcessor extends AbstractProcessor { @@ -38,22 +39,28 @@ public class MixinProcessor extends AbstractProcessor { @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - if(roundEnv.processingOver()){ - filterMixinSets(); - // create record for serialization, compute package name - String packageName = mixinConfigList.get("mixins").get(0).split("(?<=mixin)")[0]; - finalizeMixinConfig(); - new MixinConfig(packageName, - mixinConfigList.get("mixins"), - mixinConfigList.get("client") - ).generateMixinConfig(processingEnv); - } else { - processMixins(annotations, roundEnv); + try { + if(roundEnv.processingOver()){ + filterMixinSets(); + // create record for serialization, compute package name + String packageName = mixinConfigList.get("mixins").get(0).split("(?<=mixin)")[0]; + finalizeMixinConfig(); + new MixinConfig(packageName, + mixinConfigList.get("mixins"), + mixinConfigList.get("client") + ).generateMixinConfig(processingEnv); + } else { + processMixins(annotations, roundEnv); + } + } catch (Exception e) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Fatal error:" + + Throwables.getStackTraceAsString(e)); } return false; } - private void processMixins(Set annotations, RoundEnvironment roundEnv) { + private void processMixins(Set annotations, RoundEnvironment roundEnv) + throws ReflectiveOperationException { for (TypeElement annotation : annotations) { Set annotatedMixins = roundEnv.getElementsAnnotatedWith(annotation); @@ -78,7 +85,8 @@ public class MixinProcessor extends AbstractProcessor { commonSet.removeAll(mixinConfigList.get("client")); } - private void validateCommonMixins(TypeElement annotation, Stream mixins) { + private void validateCommonMixins(TypeElement annotation, Stream mixins) + throws ReflectiveOperationException { if(!annotation.getSimpleName().toString().equals("Mixin")) return; ClientMixinValidator validator = new ClientMixinValidator(processingEnv); diff --git a/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/config/MixinConfig.java b/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/config/MixinConfig.java index e8b59df0..9d1e6c91 100644 --- a/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/config/MixinConfig.java +++ b/annotation-processor/src/main/java/org/fury_phoenix/mixinAp/config/MixinConfig.java @@ -1,6 +1,5 @@ package org.fury_phoenix.mixinAp.config; -import com.google.common.base.Throwables; import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; @@ -10,7 +9,6 @@ import java.util.List; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; -import javax.tools.Diagnostic; import javax.tools.StandardLocation; public record MixinConfig( @@ -37,12 +35,12 @@ public record MixinConfig( public static final OverwriteOptions DEFAULT = new OverwriteOptions(true); } - public void generateMixinConfig(ProcessingEnvironment env) { + public void generateMixinConfig(ProcessingEnvironment env) throws IOException { try ( Writer mixinConfigWriter = env.getFiler() .createResource(StandardLocation.SOURCE_OUTPUT, "", MixinConfig.computeMixinConfigPath( - env.getOptions().get("rootProject.name"), + Optional.of(env.getOptions().get("rootProject.name")), Optional.ofNullable(env.getOptions().get("project.name")) ) ).openWriter() @@ -54,15 +52,12 @@ public record MixinConfig( mixinConfigWriter.write(mixinConfig); mixinConfigWriter.write("\n"); - } catch (IOException e) { - env.getMessager().printMessage(Diagnostic.Kind.ERROR, "Fatal error:" + - Throwables.getStackTraceAsString(e)); - } + } catch (IOException e) { throw e; } } - private static String computeMixinConfigPath(String rootProjectName, Optional projectName) { + private static String computeMixinConfigPath(Optional rootProjectName, Optional projectName) { return "resources/" + - rootProjectName + + rootProjectName.get() + (projectName.isPresent() ? "-" : "") + projectName.orElse("") + ".mixins.json";