mirror of
https://github.com/architectury/architectury-loom.git
synced 2026-03-28 04:07:01 -05:00
Add ability to remap annotations data (#1366)
* Add ability to remap annotations data * Fix unpick remap test
This commit is contained in:
@@ -134,13 +134,13 @@ public record LayeredMappingsFactory(LayeredMappingSpec spec) {
|
||||
}
|
||||
|
||||
private void writeAnnotationData(LayeredMappingsProcessor processor, List<MappingLayer> layers, Path mappingsFile) throws IOException {
|
||||
AnnotationsData annotationsData = processor.getAnnotationsData(layers);
|
||||
List<AnnotationsData> 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);
|
||||
}
|
||||
|
||||
@@ -119,20 +119,15 @@ public class LayeredMappingsProcessor {
|
||||
return mappingTree;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AnnotationsData getAnnotationsData(List<MappingLayer> layers) throws IOException {
|
||||
AnnotationsData result = null;
|
||||
public List<AnnotationsData> getAnnotationsData(List<MappingLayer> layers) throws IOException {
|
||||
List<AnnotationsData> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +77,7 @@ public class MappingConfiguration {
|
||||
public final Path tinyMappingsJar;
|
||||
private final Path unpickDefinitions;
|
||||
|
||||
@Nullable
|
||||
private AnnotationsData annotationsData;
|
||||
private List<AnnotationsData> annotationsData = List.of();
|
||||
@Nullable
|
||||
private UnpickMetadata unpickMetadata;
|
||||
private Map<String, String> 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<AnnotationsData> getAnnotationsData() {
|
||||
return annotationsData;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, ClassAnnotationData> 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<String, ClassAnnotationData> 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<String, ClassAnnotationData> classes) {
|
||||
.registerTypeAdapter(AnnotationNode.class, new AnnotationNodeSerializer())
|
||||
.registerTypeAdapterFactory(new SkipEmptyTypeAdapterFactory())
|
||||
.create();
|
||||
private static final Type LIST_TYPE = new TypeToken<List<AnnotationNode>>() { }.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<AnnotationsData> 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> 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<String, ClassAnnotationData> 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 <K, V> Map<K, V> remapMap(Map<K, V> map, Function<Map.Entry<K, V>, K> keyRemapper, Function<Map.Entry<K, V>, V> valueRemapper) {
|
||||
Map<K, V> result = LinkedHashMap.newLinkedHashMap(map.size());
|
||||
|
||||
for (Map.Entry<K, V> 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<AnnotationsData> datas = mappingConfiguration.getAnnotationsData();
|
||||
|
||||
if (datas.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, TinyRemapper> 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<String, TinyRemapper> 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> 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;
|
||||
|
||||
@@ -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<String> 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;
|
||||
|
||||
@@ -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<String> 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;
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,15 +244,14 @@ public abstract class AbstractMappedMinecraftProvider<M extends MinecraftProvide
|
||||
|
||||
Files.deleteIfExists(remappedJars.outputJarPath());
|
||||
|
||||
final AnnotationsData remappedAnnotations = AnnotationsData.getRemappedAnnotations(getTargetNamespace(), mappingConfiguration, getProject(), configContext.serviceFactory(), toM);
|
||||
final Map<String, String> 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));
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
"""
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user