From 213bbfcd18342360076d0efac3b12bfffae3a43f Mon Sep 17 00:00:00 2001 From: Joseph Burton Date: Sat, 20 Sep 2025 13:17:37 +0100 Subject: [PATCH] Add ability to remap annotations data (#1366) * Add ability to remap annotations data * Fix unpick remap test --- .../mappings/LayeredMappingsFactory.java | 6 +- .../mappings/LayeredMappingsProcessor.java | 11 +- .../mappings/MappingConfiguration.java | 8 +- .../extras/annotations/AnnotationsData.java | 133 ++++++++- .../annotations/ClassAnnotationData.java | 47 ++++ .../annotations/GenericAnnotationData.java | 12 + .../annotations/MethodAnnotationData.java | 17 ++ .../extras/annotations/TypeAnnotationKey.java | 5 + .../AbstractMappedMinecraftProvider.java | 7 +- .../AnnotationsDataRemapTest.groovy | 259 ++++++++++++++++++ .../AnnotationsLayerTest.groovy | 21 +- .../service/mocks/MockTinyRemapper.groovy | 5 +- src/test/resources/unpick/remapped.unpick | 4 +- 13 files changed, 496 insertions(+), 39 deletions(-) create mode 100644 src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsDataRemapTest.groovy diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsFactory.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsFactory.java index de3d43f0..714028f2 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsFactory.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsFactory.java @@ -134,13 +134,13 @@ public record LayeredMappingsFactory(LayeredMappingSpec spec) { } private void writeAnnotationData(LayeredMappingsProcessor processor, List layers, Path mappingsFile) throws IOException { - AnnotationsData annotationsData = processor.getAnnotationsData(layers); + List annotationsData = processor.getAnnotationsData(layers); - if (annotationsData == null) { + if (annotationsData.isEmpty()) { return; } - byte[] data = AnnotationsData.GSON.toJson(annotationsData.toJson()).getBytes(StandardCharsets.UTF_8); + byte[] data = AnnotationsData.GSON.toJson(AnnotationsData.listToJson(annotationsData)).getBytes(StandardCharsets.UTF_8); ZipUtils.add(mappingsFile, AnnotationsLayer.ANNOTATIONS_PATH, data); } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java index 54afa31a..3aa6736f 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/LayeredMappingsProcessor.java @@ -119,20 +119,15 @@ public class LayeredMappingsProcessor { return mappingTree; } - @Nullable - public AnnotationsData getAnnotationsData(List layers) throws IOException { - AnnotationsData result = null; + public List getAnnotationsData(List layers) throws IOException { + List result = new ArrayList<>(); for (MappingLayer layer : layers) { if (layer instanceof AnnotationsLayer annotationsLayer) { AnnotationsData annotationsData = annotationsLayer.getAnnotationsData(); if (annotationsData != null) { - if (result == null) { - result = annotationsData; - } else { - result = result.merge(annotationsData); - } + result.add(annotationsData); } } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java index f1b654c7..58e4dbb4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/MappingConfiguration.java @@ -77,8 +77,7 @@ public class MappingConfiguration { public final Path tinyMappingsJar; private final Path unpickDefinitions; - @Nullable - private AnnotationsData annotationsData; + private List annotationsData = List.of(); @Nullable private UnpickMetadata unpickMetadata; private Map signatureFixes; @@ -233,7 +232,7 @@ public class MappingConfiguration { } try (BufferedReader reader = Files.newBufferedReader(annotationsPath, StandardCharsets.UTF_8)) { - annotationsData = AnnotationsData.read(reader); + annotationsData = AnnotationsData.readList(reader); } } @@ -312,8 +311,7 @@ public class MappingConfiguration { return unpickMetadata != null; } - @Nullable - public AnnotationsData getAnnotationsData() { + public List getAnnotationsData() { return annotationsData; } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/AnnotationsData.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/AnnotationsData.java index 27b516d9..0e834a11 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/AnnotationsData.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/AnnotationsData.java @@ -24,19 +24,35 @@ package net.fabricmc.loom.configuration.providers.mappings.extras.annotations; +import java.io.IOException; import java.io.Reader; +import java.lang.reflect.Type; +import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.function.Function; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSyntaxException; +import com.google.gson.reflect.TypeToken; +import org.gradle.api.Project; +import org.jetbrains.annotations.Nullable; +import org.objectweb.asm.commons.AnnotationRemapper; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.TypeAnnotationNode; -public record AnnotationsData(Map classes) { +import net.fabricmc.loom.api.mappings.layered.MappingsNamespace; +import net.fabricmc.loom.configuration.providers.mappings.MappingConfiguration; +import net.fabricmc.loom.util.TinyRemapperHelper; +import net.fabricmc.loom.util.service.ServiceFactory; +import net.fabricmc.tinyremapper.TinyRemapper; + +public record AnnotationsData(Map classes, String namespace) { public static final Gson GSON = new GsonBuilder() .disableHtmlEscaping() .setFieldNamingStrategy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) @@ -45,34 +61,139 @@ public record AnnotationsData(Map classes) { .registerTypeAdapter(AnnotationNode.class, new AnnotationNodeSerializer()) .registerTypeAdapterFactory(new SkipEmptyTypeAdapterFactory()) .create(); + private static final Type LIST_TYPE = new TypeToken>() { }.getType(); + private static final int CURRENT_VERSION = 1; + + public AnnotationsData { + if (namespace == null) { + namespace = MappingsNamespace.NAMED.toString(); + } + } public static AnnotationsData read(Reader reader) { JsonObject json = GSON.fromJson(reader, JsonObject.class); + checkVersion(json); + return GSON.fromJson(json, AnnotationsData.class); + } + public static List readList(Reader reader) { + JsonObject json = GSON.fromJson(reader, JsonObject.class); + checkVersion(json); + JsonElement values = json.get("values"); + + if (values == null || values.isJsonNull()) { + return List.of(GSON.fromJson(json, AnnotationsData.class)); + } + + return GSON.fromJson(values, LIST_TYPE); + } + + private static void checkVersion(JsonObject json) { if (!json.has("version")) { throw new JsonSyntaxException("Missing annotations version"); } int version = json.getAsJsonPrimitive("version").getAsInt(); - if (version != 1) { + if (version != CURRENT_VERSION) { throw new JsonSyntaxException("Invalid annotations version " + version + ". Try updating loom"); } - - return GSON.fromJson(json, AnnotationsData.class); } public JsonObject toJson() { JsonObject json = GSON.toJsonTree(this).getAsJsonObject(); JsonObject result = new JsonObject(); - result.addProperty("version", 1); + result.addProperty("version", CURRENT_VERSION); result.asMap().putAll(json.asMap()); return result; } + public static JsonObject listToJson(List annotationsData) { + if (annotationsData.size() == 1) { + return annotationsData.getFirst().toJson(); + } + + JsonObject result = new JsonObject(); + result.addProperty("version", CURRENT_VERSION); + result.add("values", GSON.toJsonTree(annotationsData)); + return result; + } + public AnnotationsData merge(AnnotationsData other) { + if (!namespace.equals(other.namespace)) { + throw new IllegalArgumentException("Cannot merge annotations from namespace " + other.namespace + " into annotations from namespace " + this.namespace); + } + Map newClassData = new LinkedHashMap<>(classes); other.classes.forEach((key, value) -> newClassData.merge(key, value, ClassAnnotationData::merge)); - return new AnnotationsData(newClassData); + return new AnnotationsData(newClassData, namespace); + } + + public AnnotationsData remap(TinyRemapper remapper, String newNamespace) { + return new AnnotationsData( + remapMap( + classes, + entry -> remapper.getEnvironment().getRemapper().map(entry.getKey()), + entry -> entry.getValue().remap(entry.getKey(), remapper) + ), + newNamespace + ); + } + + static AnnotationNode remap(AnnotationNode node, TinyRemapper remapper) { + AnnotationNode remapped = new AnnotationNode(remapper.getEnvironment().getRemapper().mapDesc(node.desc)); + node.accept(new AnnotationRemapper(node.desc, remapped, remapper.getEnvironment().getRemapper())); + return remapped; + } + + static TypeAnnotationNode remap(TypeAnnotationNode node, TinyRemapper remapper) { + TypeAnnotationNode remapped = new TypeAnnotationNode(node.typeRef, node.typePath, remapper.getEnvironment().getRemapper().mapDesc(node.desc)); + node.accept(new AnnotationRemapper(node.desc, remapped, remapper.getEnvironment().getRemapper())); + return remapped; + } + + static Map remapMap(Map map, Function, K> keyRemapper, Function, V> valueRemapper) { + Map result = LinkedHashMap.newLinkedHashMap(map.size()); + + for (Map.Entry entry : map.entrySet()) { + if (result.put(keyRemapper.apply(entry), valueRemapper.apply(entry)) != null) { + throw new IllegalStateException("Remapping annotations resulted in duplicate key: " + keyRemapper.apply(entry)); + } + } + + return result; + } + + @Nullable + public static AnnotationsData getRemappedAnnotations(MappingsNamespace targetNamespace, MappingConfiguration mappingConfiguration, Project project, ServiceFactory serviceFactory, String newNamespace) throws IOException { + List datas = mappingConfiguration.getAnnotationsData(); + + if (datas.isEmpty()) { + return null; + } + + Map existingRemappers = new HashMap<>(); + AnnotationsData result = datas.getFirst().remap(targetNamespace, project, serviceFactory, newNamespace, existingRemappers); + + for (int i = 1; i < datas.size(); i++) { + result = result.merge(datas.get(i).remap(targetNamespace, project, serviceFactory, newNamespace, existingRemappers)); + } + + return result; + } + + private AnnotationsData remap(MappingsNamespace targetNamespace, Project project, ServiceFactory serviceFactory, String newNamespace, Map existingRemappers) throws IOException { + if (namespace.equals(targetNamespace.toString())) { + return this; + } + + TinyRemapper remapper = existingRemappers.get(namespace); + + if (remapper == null) { + remapper = TinyRemapperHelper.getTinyRemapper(project, serviceFactory, namespace, newNamespace); + existingRemappers.put(namespace, remapper); + } + + return remap(remapper, newNamespace); } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/ClassAnnotationData.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/ClassAnnotationData.java index dc0d2651..14ed43c5 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/ClassAnnotationData.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/ClassAnnotationData.java @@ -30,6 +30,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import com.google.gson.annotations.SerializedName; import org.jetbrains.annotations.Nullable; @@ -37,6 +38,9 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.TypeAnnotationNode; +import net.fabricmc.tinyremapper.TinyRemapper; +import net.fabricmc.tinyremapper.api.TrRemapper; + public record ClassAnnotationData( @SerializedName("remove") Set annotationsToRemove, @@ -90,6 +94,49 @@ public record ClassAnnotationData( return new ClassAnnotationData(newAnnotationsToRemove, newAnnotationsToAdd, newTypeAnnotationsToRemove, newTypeAnnotationsToAdd, newFields, newMethods); } + ClassAnnotationData remap(String className, TinyRemapper remapper) { + return new ClassAnnotationData( + annotationsToRemove.stream().map(remapper.getEnvironment().getRemapper()::map).collect(Collectors.toCollection(LinkedHashSet::new)), + annotationsToAdd.stream().map(ann -> AnnotationsData.remap(ann, remapper)).collect(Collectors.toCollection(ArrayList::new)), + typeAnnotationsToRemove.stream().map(key -> key.remap(remapper)).collect(Collectors.toCollection(LinkedHashSet::new)), + typeAnnotationsToAdd.stream().map(ann -> AnnotationsData.remap(ann, remapper)).collect(Collectors.toCollection(ArrayList::new)), + AnnotationsData.remapMap( + fields, + entry -> remapField(className, entry.getKey(), remapper), + entry -> entry.getValue().remap(remapper) + ), + AnnotationsData.remapMap( + methods, + entry -> remapMethod(className, entry.getKey(), remapper), + entry -> entry.getValue().remap(remapper) + ) + ); + } + + private static String remapField(String className, String field, TinyRemapper remapper) { + String[] nameDesc = field.split(":", 2); + + if (nameDesc.length != 2) { + return field; + } + + TrRemapper trRemapper = remapper.getEnvironment().getRemapper(); + return trRemapper.mapFieldName(className, nameDesc[0], nameDesc[1]) + ":" + trRemapper.mapDesc(nameDesc[1]); + } + + private static String remapMethod(String className, String method, TinyRemapper remapper) { + int parenIndex = method.indexOf('('); + + if (parenIndex == -1) { + return method; + } + + String name = method.substring(0, parenIndex); + String desc = method.substring(parenIndex); + TrRemapper trRemapper = remapper.getEnvironment().getRemapper(); + return trRemapper.mapMethodName(className, name, desc) + trRemapper.mapMethodDesc(desc); + } + public int modifyAccessFlags(int access) { if (annotationsToRemove.contains("java/lang/Deprecated")) { access &= ~Opcodes.ACC_DEPRECATED; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/GenericAnnotationData.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/GenericAnnotationData.java index 4c858811..94a2e96e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/GenericAnnotationData.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/GenericAnnotationData.java @@ -28,12 +28,15 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import com.google.gson.annotations.SerializedName; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.TypeAnnotationNode; +import net.fabricmc.tinyremapper.TinyRemapper; + public record GenericAnnotationData( @SerializedName("remove") Set annotationsToRemove, @@ -74,6 +77,15 @@ public record GenericAnnotationData( return new GenericAnnotationData(newAnnotationToRemove, newAnnotationsToAdd, newTypeAnnotationsToRemove, newTypeAnnotationsToAdd); } + GenericAnnotationData remap(TinyRemapper remapper) { + return new GenericAnnotationData( + annotationsToRemove.stream().map(remapper.getEnvironment().getRemapper()::map).collect(Collectors.toCollection(LinkedHashSet::new)), + annotationsToAdd.stream().map(ann -> AnnotationsData.remap(ann, remapper)).collect(Collectors.toCollection(ArrayList::new)), + typeAnnotationsToRemove.stream().map(key -> key.remap(remapper)).collect(Collectors.toCollection(LinkedHashSet::new)), + typeAnnotationsToAdd.stream().map(ann -> AnnotationsData.remap(ann, remapper)).collect(Collectors.toCollection(ArrayList::new)) + ); + } + public int modifyAccessFlags(int access) { if (annotationsToRemove.contains("java/lang/Deprecated")) { access &= ~Opcodes.ACC_DEPRECATED; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/MethodAnnotationData.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/MethodAnnotationData.java index 45bde212..fde79765 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/MethodAnnotationData.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/MethodAnnotationData.java @@ -30,12 +30,15 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import com.google.gson.annotations.SerializedName; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.TypeAnnotationNode; +import net.fabricmc.tinyremapper.TinyRemapper; + public record MethodAnnotationData( @SerializedName("remove") Set annotationsToRemove, @@ -83,6 +86,20 @@ public record MethodAnnotationData( return new MethodAnnotationData(newAnnotationsToRemove, newAnnotationsToAdd, newTypeAnnotationsToRemove, newTypeAnnotationsToAdd, newParameters); } + MethodAnnotationData remap(TinyRemapper remapper) { + return new MethodAnnotationData( + annotationsToRemove.stream().map(remapper.getEnvironment().getRemapper()::map).collect(Collectors.toCollection(LinkedHashSet::new)), + annotationsToAdd.stream().map(ann -> AnnotationsData.remap(ann, remapper)).collect(Collectors.toCollection(ArrayList::new)), + typeAnnotationsToRemove.stream().map(key -> key.remap(remapper)).collect(Collectors.toCollection(LinkedHashSet::new)), + typeAnnotationsToAdd.stream().map(ann -> AnnotationsData.remap(ann, remapper)).collect(Collectors.toCollection(ArrayList::new)), + AnnotationsData.remapMap( + parameters, + Map.Entry::getKey, + entry -> entry.getValue().remap(remapper) + ) + ); + } + public int modifyAccessFlags(int access) { if (annotationsToRemove.contains("java/lang/Deprecated")) { access &= ~Opcodes.ACC_DEPRECATED; diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/TypeAnnotationKey.java b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/TypeAnnotationKey.java index e2b4307f..a58e5fb5 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/TypeAnnotationKey.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/mappings/extras/annotations/TypeAnnotationKey.java @@ -24,5 +24,10 @@ package net.fabricmc.loom.configuration.providers.mappings.extras.annotations; +import net.fabricmc.tinyremapper.TinyRemapper; + public record TypeAnnotationKey(int typeRef, String typePath, String name) { + TypeAnnotationKey remap(TinyRemapper remapper) { + return new TypeAnnotationKey(typeRef, typePath, remapper.getEnvironment().getRemapper().map(name)); + } } diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java index 819cb195..10fca71d 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/minecraft/mapped/AbstractMappedMinecraftProvider.java @@ -244,15 +244,14 @@ public abstract class AbstractMappedMinecraftProvider remappedSignatures = SignatureFixerApplyVisitor.getRemappedSignatures(getTargetNamespace() == MappingsNamespace.INTERMEDIARY, mappingConfiguration, getProject(), configContext.serviceFactory(), toM); final MinecraftVersionMeta.JavaVersion javaVersion = minecraftProvider.getVersionInfo().javaVersion(); final boolean fixRecords = javaVersion != null && javaVersion.majorVersion() >= 16; TinyRemapper remapper = TinyRemapperHelper.getTinyRemapper(getProject(), configContext.serviceFactory(), fromM, toM, fixRecords, (builder) -> { - AnnotationsData annotationsData = mappingConfiguration.getAnnotationsData(); - - if (annotationsData != null) { - builder.extraPostApplyVisitor(new AnnotationsApplyVisitor(annotationsData)); + if (remappedAnnotations != null) { + builder.extraPostApplyVisitor(new AnnotationsApplyVisitor(remappedAnnotations)); } builder.extraPostApplyVisitor(new SignatureFixerApplyVisitor(remappedSignatures)); diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsDataRemapTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsDataRemapTest.groovy new file mode 100644 index 00000000..44b4b491 --- /dev/null +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsDataRemapTest.groovy @@ -0,0 +1,259 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2025 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.unit.layeredmappings + +import org.intellij.lang.annotations.Language +import spock.lang.Specification + +import net.fabricmc.loom.configuration.providers.mappings.extras.annotations.AnnotationsData +import net.fabricmc.loom.test.unit.service.mocks.MockTinyRemapper +import net.fabricmc.tinyremapper.api.TrClass +import net.fabricmc.tinyremapper.api.TrField +import net.fabricmc.tinyremapper.api.TrMethod + +import static org.mockito.Mockito.* + +class AnnotationsDataRemapTest extends Specification { + def "remap annotations data"() { + given: + def reader = new BufferedReader(new StringReader(ANNOTATIONS)) + def annotationsData = AnnotationsData.read(reader) + + def mockTr = new MockTinyRemapper() + + when(mockTr.remapper.map("pkg/Foo")).thenReturn("mapped/pkg/FooMapped") + when(mockTr.remapper.map("pkg/Bar")).thenReturn("mapped/pkg/BarMapped") + + when(mockTr.remapper.map("pkg/Annotation1")).thenReturn("mapped/pkg/Annotation1Mapped") + when(mockTr.remapper.map("pkg/Annotation2")).thenReturn("mapped/pkg/Annotation2Mapped") + when(mockTr.remapper.map("pkg/Annotation3")).thenReturn("mapped/pkg/Annotation3Mapped") + when(mockTr.remapper.map("pkg/Annotation4")).thenReturn("mapped/pkg/Annotation4Mapped") + when(mockTr.remapper.map("pkg/Annotation5")).thenReturn("mapped/pkg/Annotation5Mapped") + when(mockTr.remapper.map("pkg/Annotation6")).thenReturn("mapped/pkg/Annotation6Mapped") + when(mockTr.remapper.map("pkg/Annotation7")).thenReturn("mapped/pkg/Annotation7Mapped") + when(mockTr.remapper.map("pkg/Annotation8")).thenReturn("mapped/pkg/Annotation8Mapped") + + when(mockTr.remapper.map("pkg/MyEnum")).thenReturn("mapped/pkg/MyEnumMapped") + + when(mockTr.remapper.map("baz")).thenReturn("mapped/baz") + + when(mockTr.remapper.mapFieldName("pkg/Foo", "bar", "Lbaz;")).thenReturn("barRenamed") + when(mockTr.remapper.mapMethodName("pkg/Foo", "bar", "()V")).thenReturn("barMethodRenamed") + + def mockClass = mock(TrClass.class) + when(mockTr.trEnvironment.getClass("pkg/Foo")).thenReturn(mockClass) + + def mockField = mock(TrField.class) + when(mockField.name).thenReturn("bar") + when(mockField.desc).thenReturn("Lbaz;") + when(mockClass.fields).thenReturn([mockField]) + + def mockMethod = mock(TrMethod.class) + when(mockMethod.name).thenReturn("bar") + when(mockMethod.desc).thenReturn("()V") + when(mockClass.methods).thenReturn([mockMethod]) + + when: + def remapped = annotationsData.remap(mockTr.tinyRemapper, "mapped") + + then: + def json = AnnotationsData.GSON.newBuilder() + .setPrettyPrinting() + .create() + .toJson(remapped.toJson()) + .replace(" ", "\t") + + json == REMAPPED_ANNOTATIONS.trim() + } + + @Language("JSON") + private static final String ANNOTATIONS = """ +{ + "version": 1, + "classes": { + "pkg/Foo": { + "remove": [ + "pkg/Annotation1", + "pkg/Annotation2", + "pkg/Annotation3" + ], + "add": [ + { + "desc": "Lpkg/Annotation4;" + }, + { + "desc": "Lpkg/Annotation5;", + "values": { + "foo": { + "type": "int", + "value": 42 + }, + "bar": { + "type": "class", + "value": "Ljava/lang/String;" + }, + "baz": { + "type": "enum_constant", + "owner": "Lpkg/MyEnum;", + "name": "VALUE" + }, + "ann": { + "type": "annotation", + "desc": "Lpkg/Annotation6;" + }, + "arr": { + "type": "array", + "value": [ + { + "type": "int", + "value": 1 + }, + { + "type": "int", + "value": 2 + } + ] + } + } + } + ], + "type_add": [ + { + "desc": "Lpkg/Annotation7;", + "type_ref": 22, + "type_path": "[" + } + ], + "fields": { + "bar:Lbaz;": { + "remove": [ + "pkg/Annotation8" + ] + } + }, + "methods": { + "bar()V": { + "remove": [ + "pkg/Annotation8" + ] + } + } + }, + "pkg/Bar": { + "add": [ + { + "desc": "Lpkg/Annotation1;" + } + ] + } + }, + "namespace": "someNamespace" +} +""" + @Language("JSON") + private static final String REMAPPED_ANNOTATIONS = """ +{ + "version": 1, + "classes": { + "mapped/pkg/FooMapped": { + "remove": [ + "mapped/pkg/Annotation1Mapped", + "mapped/pkg/Annotation2Mapped", + "mapped/pkg/Annotation3Mapped" + ], + "add": [ + { + "desc": "Lmapped/pkg/Annotation4Mapped;" + }, + { + "desc": "Lmapped/pkg/Annotation5Mapped;", + "values": { + "foo": { + "type": "int", + "value": 42 + }, + "bar": { + "type": "class", + "value": "Ljava/lang/String;" + }, + "baz": { + "type": "enum_constant", + "owner": "Lmapped/pkg/MyEnumMapped;", + "name": "VALUE" + }, + "ann": { + "type": "annotation", + "desc": "Lmapped/pkg/Annotation6Mapped;" + }, + "arr": { + "type": "array", + "value": [ + { + "type": "int", + "value": 1 + }, + { + "type": "int", + "value": 2 + } + ] + } + } + } + ], + "type_add": [ + { + "desc": "Lmapped/pkg/Annotation7Mapped;", + "type_ref": 22, + "type_path": "[" + } + ], + "fields": { + "barRenamed:Lmapped/baz;": { + "remove": [ + "mapped/pkg/Annotation8Mapped" + ] + } + }, + "methods": { + "barMethodRenamed()V": { + "remove": [ + "mapped/pkg/Annotation8Mapped" + ] + } + } + }, + "mapped/pkg/BarMapped": { + "add": [ + { + "desc": "Lmapped/pkg/Annotation1Mapped;" + } + ] + } + }, + "namespace": "mapped" +} +""" +} diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsLayerTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsLayerTest.groovy index fb5b1fdf..b9879b77 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsLayerTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/layeredmappings/AnnotationsLayerTest.groovy @@ -44,11 +44,11 @@ class AnnotationsLayerTest extends Specification { "pkg/Annotation2", "pkg/Annotation3" ] as Set - annotationsData.classes()["pkg/Foo"].annotationsToAdd()[0].desc == "pkg/Annotation4" + annotationsData.classes()["pkg/Foo"].annotationsToAdd()[0].desc == "Lpkg/Annotation4;" annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[1] == 42 annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[3] == Type.getType("Ljava/lang/String;") - annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[5] == ["pkg/MyEnum", "VALUE"] as String[] - annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[7] instanceof AnnotationNode && annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[7].desc == "pkg/Annotation6" + annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[5] == ["Lpkg/MyEnum;", "VALUE"] as String[] + annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[7] instanceof AnnotationNode && annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[7].desc == "Lpkg/Annotation6;" annotationsData.classes()["pkg/Foo"].annotationsToAdd()[1].values[9] == [1, 2] annotationsData.classes()["pkg/Foo"].typeAnnotationsToAdd()[0].typePath.toString() == "[" annotationsData.classes()["pkg/Foo"].fields().keySet().first() == "bar:Lbaz;" @@ -83,10 +83,10 @@ class AnnotationsLayerTest extends Specification { ], "add": [ { - "desc": "pkg/Annotation4" + "desc": "Lpkg/Annotation4;" }, { - "desc": "pkg/Annotation5", + "desc": "Lpkg/Annotation5;", "values": { "foo": { "type": "int", @@ -98,12 +98,12 @@ class AnnotationsLayerTest extends Specification { }, "baz": { "type": "enum_constant", - "owner": "pkg/MyEnum", + "owner": "Lpkg/MyEnum;", "name": "VALUE" }, "ann": { "type": "annotation", - "desc": "pkg/Annotation6" + "desc": "Lpkg/Annotation6;" }, "arr": { "type": "array", @@ -123,7 +123,7 @@ class AnnotationsLayerTest extends Specification { ], "type_add": [ { - "desc": "pkg/Annotation7", + "desc": "Lpkg/Annotation7;", "type_ref": 22, "type_path": "[" } @@ -146,11 +146,12 @@ class AnnotationsLayerTest extends Specification { "pkg/Bar": { "add": [ { - "desc": "pkg/Annotation1" + "desc": "Lpkg/Annotation1;" } ] } - } + }, + "namespace": "someNamespace" } """ } diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/service/mocks/MockTinyRemapper.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/service/mocks/MockTinyRemapper.groovy index 9721c806..5fec4a1b 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/service/mocks/MockTinyRemapper.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/service/mocks/MockTinyRemapper.groovy @@ -24,17 +24,20 @@ package net.fabricmc.loom.test.unit.service.mocks +import org.mockito.Answers + import net.fabricmc.tinyremapper.TinyRemapper import net.fabricmc.tinyremapper.api.TrEnvironment import net.fabricmc.tinyremapper.api.TrRemapper import static org.mockito.Mockito.mock import static org.mockito.Mockito.when +import static org.mockito.Mockito.withSettings class MockTinyRemapper { TinyRemapper tinyRemapper = mock(TinyRemapper.class) TrEnvironment trEnvironment = mock(TrEnvironment.class) - TrRemapper remapper = mock(TrRemapper.class) + TrRemapper remapper = mock(TrRemapper.class, withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS)) MockTinyRemapper() { when(tinyRemapper.getEnvironment()).thenReturn(trEnvironment) diff --git a/src/test/resources/unpick/remapped.unpick b/src/test/resources/unpick/remapped.unpick index a3dab5e2..a25355da 100644 --- a/src/test/resources/unpick/remapped.unpick +++ b/src/test/resources/unpick/remapped.unpick @@ -2,7 +2,7 @@ unpick v3 target_field mapped.bar.Y quux I g -target_field mapped.bar.Z null Lmapped/foo/X; g +target_field mapped.bar.Z foo Lmapped/foo/X; g target_method mapped.bar.Y bar2 (Lmapped/foo/X;)V @@ -13,7 +13,7 @@ group float mapped.bar.Y.quux:int group float - mapped.bar.Y.*:float + mapped.bar.Y.baz:float group int @scope class mapped.bar.Y