Skip to content

Commit

Permalink
more work on renaming
Browse files Browse the repository at this point in the history
  • Loading branch information
Machine-Maker committed Sep 28, 2024
1 parent 5569ad7 commit 51a731b
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 108 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ val filtered = tasks.register<FilterTestClasspath>("filteredTestClasspath") {

dependencies {
implementation(mainForNewTargets.output)
testImplementation(files(filtered.flatMap { it.outputDir }))
testRuntimeOnly(files(filtered.flatMap { it.outputDir })) // only have access to old targets at runtime, don't use them in actual tests
testImplementation(testDataNewTargets.output)

testDataNewTargets.implementationConfigurationName(mainForNewTargets.output)
Expand Down
18 changes: 7 additions & 11 deletions src/main/java/io/papermc/asm/rules/rename/EnumRenameBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@
public final class EnumRenameBuilder {

private final ClassDesc enumTypeDesc;
private @Nullable ClassDesc optionalEnumReplacementImpl;
private @Nullable ClassDesc alternateValueOfOwner;
private final Map<String, String> enumFieldRenames = new HashMap<>();

EnumRenameBuilder(final ClassDesc enumTypeDesc) {
this.enumTypeDesc = enumTypeDesc;
}

public EnumRenameBuilder enumReplacementImpl(final Class<?> type) {
return this.enumReplacementImpl(desc(type));
public EnumRenameBuilder alternateValueOfOwner(final Class<?> type) {
return this.alternateValueOfOwner(desc(type));
}

public EnumRenameBuilder enumReplacementImpl(final ClassDesc type) {
public EnumRenameBuilder alternateValueOfOwner(final ClassDesc type) {
if (this.enumTypeDesc.equals(type)) {
throw new IllegalArgumentException("Cannot replace an enum with itself");
}
this.optionalEnumReplacementImpl = type;
this.alternateValueOfOwner = type;
return this;
}

Expand All @@ -34,11 +34,7 @@ public EnumRenameBuilder rename(final String legacyName, final String newName) {
return this;
}

void apply(final RenameRuleBuilder renameRuleBuilder) {
this.enumFieldRenames.forEach((legacyName, newName) -> {
renameRuleBuilder.fieldByDesc(this.enumTypeDesc, legacyName, newName);
});
final Map<String, String> copy = Map.copyOf(this.enumFieldRenames);
renameRuleBuilder.enumValueOfFieldRenames.put(this.enumTypeDesc, new EnumRenamer(this.enumTypeDesc, this.optionalEnumReplacementImpl, copy));
EnumRenamer build() {
return new EnumRenamer(this.enumTypeDesc, this.alternateValueOfOwner, Map.copyOf(this.enumFieldRenames));
}
}
20 changes: 19 additions & 1 deletion src/main/java/io/papermc/asm/rules/rename/EnumRenamer.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
package io.papermc.asm.rules.rename;

import java.lang.constant.ClassDesc;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.checkerframework.checker.nullness.qual.Nullable;

public record EnumRenamer(ClassDesc typeDesc, @Nullable ClassDesc optionalReplacementImpl, Map<String, String> fieldRenames) {
public record EnumRenamer(ClassDesc typeDesc, @Nullable ClassDesc alternateValueOfOwner, Map<String, String> fieldRenames) {

public EnumRenamer {
fieldRenames = Map.copyOf(fieldRenames);
}

EnumRenamer overwrite(final EnumRenamer other) {
if (!this.typeDesc.equals(other.typeDesc)) {
throw new IllegalArgumentException("Cannot merge EnumRenamers with different typeDesc");
} else if (!Objects.equals(this.alternateValueOfOwner, other.alternateValueOfOwner)) {
throw new IllegalArgumentException("Cannot merge EnumRenamers with different alternateValueOfOwner");
}
final Map<String, String> newFieldRenames = new HashMap<>();
newFieldRenames.putAll(this.fieldRenames);
newFieldRenames.putAll(other.fieldRenames);
return new EnumRenamer(this.typeDesc, this.alternateValueOfOwner, newFieldRenames);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ final class EnumValueOfRewriteRule implements GeneratedStaticRewrite {

EnumValueOfRewriteRule(final EnumRenamer renamer) {
this.owners.add(renamer.typeDesc());
if (renamer.optionalReplacementImpl() != null) {
this.owners.add(renamer.optionalReplacementImpl());
if (renamer.alternateValueOfOwner() != null) {
this.owners.add(renamer.alternateValueOfOwner());
}
this.matcher = MethodMatcher.builder()
.match("valueOf", b -> b.statik().desc(MethodTypeDesc.of(renamer.typeDesc(), ConstantDescs.CD_String)))
Expand Down
65 changes: 54 additions & 11 deletions src/main/java/io/papermc/asm/rules/rename/RenameRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,78 @@

import io.papermc.asm.rules.RewriteRule;
import io.papermc.asm.rules.rename.asm.FixedClassRemapper;
import io.papermc.asm.versioned.ApiVersion;
import io.papermc.asm.versioned.VersionedRuleFactory;
import java.lang.constant.ClassDesc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.SimpleRemapper;

public final class RenameRule implements RewriteRule.Delegate {

private final RewriteRule rule;
public static RenameRuleBuilder builder() {
return new RenameRuleBuilderImpl();
}

public RenameRule(final Map<String, String> renames, final Map<ClassDesc, EnumRenamer> enumValueOfFieldRenames) {
final Remapper remapper = new SimpleRemapper(Map.copyOf(renames));
private final Map<String, String> renames;
private final Map<ClassDesc, EnumRenamer> enumFieldRenames;
private @MonotonicNonNull RewriteRule rule;

final List<RewriteRule> rules = new ArrayList<>(enumValueOfFieldRenames.size() + 1);
enumValueOfFieldRenames.forEach((classDesc, enumRenamer) -> {
rules.add(new EnumValueOfRewriteRule(enumRenamer));
});
rules.add((api, parent, context) -> new FixedClassRemapper(api, parent, remapper));
public RenameRule(final Map<String, String> renames, final Map<ClassDesc, EnumRenamer> enumFieldRenames) {
this.renames = Map.copyOf(renames);
this.enumFieldRenames = Map.copyOf(enumFieldRenames);
}

this.rule = RewriteRule.chain(rules);
public Map<String, String> renames() {
return this.renames;
}

public static RenameRuleBuilder builder() {
return new RenameRuleBuilder();
public Map<ClassDesc, EnumRenamer> enumFieldRenames() {
return this.enumFieldRenames;
}

@Override
public RewriteRule delegate() {
if (this.rule == null) {
final Remapper remapper = new SimpleRemapper(Map.copyOf(this.renames));

final List<RewriteRule> rules = new ArrayList<>(this.enumFieldRenames.size() + 1);
this.enumFieldRenames.forEach((classDesc, enumRenamer) -> {
rules.add(new EnumValueOfRewriteRule(enumRenamer));
});
rules.add((api, parent, context) -> new FixedClassRemapper(api, parent, remapper));

this.rule = RewriteRule.chain(rules);
}
return this.rule;
}

public record Versioned(NavigableMap<ApiVersion, RenameRule> versions) implements VersionedRuleFactory {

@Override
public RewriteRule createRule(final ApiVersion apiVersion) {
final List<RenameRule> toMerge = new ArrayList<>(this.versions.tailMap(apiVersion, true).values());
if (toMerge.isEmpty()) {
return RewriteRule.EMPTY;
} else if (toMerge.size() == 1) {
return toMerge.get(0);
}
Collections.reverse(toMerge);
final Map<String, String> regularRenames = new HashMap<>();
final Map<ClassDesc, EnumRenamer> enumFieldRenames = new HashMap<>();
for (final RenameRule renameRule : toMerge) {
regularRenames.putAll(renameRule.renames);
renameRule.enumFieldRenames.forEach((classDesc, renamer) -> {
enumFieldRenames.merge(classDesc, renamer, EnumRenamer::overwrite);
});
}
return new RenameRule(regularRenames, enumFieldRenames);
}
}
}
98 changes: 34 additions & 64 deletions src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilder.java
Original file line number Diff line number Diff line change
@@ -1,116 +1,86 @@
package io.papermc.asm.rules.rename;

import io.papermc.asm.util.Builder;
import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;

import static io.papermc.asm.util.DescriptorUtils.desc;
import static io.papermc.asm.util.DescriptorUtils.toOwner;

public final class RenameRuleBuilder implements io.papermc.asm.util.Builder<RenameRule> {
public interface RenameRuleBuilder extends Builder<RenameRule> {

RenameRuleBuilder() {
}

final Map<String, String> mappings = new HashMap<>();
final Map<ClassDesc, EnumRenamer> enumValueOfFieldRenames = new HashMap<>();

public RenameRuleBuilder methodByDesc(final Iterable<ClassDesc> owners, final String legacyMethodName, final MethodTypeDesc desc, final String newMethodName) {
for (final ClassDesc owner : owners) {
this.methodByDesc(owner, legacyMethodName, desc, newMethodName);
//<editor-fold desc="methods" defaultstate="collapsed">
default RenameRuleBuilder methodByClass(final Set<Class<?>> owners, final String legacyMethodName, final MethodTypeDesc methodDesc, final String newMethodName) {
for (final Class<?> owner : owners) {
this.methodByClass(owner, legacyMethodName, methodDesc, newMethodName);
}
return this;
}

public RenameRuleBuilder methodByDesc(final ClassDesc owner, final String legacyMethodName, final MethodTypeDesc desc, final String newMethodName) {
return this.methodByInternal(toOwner(owner), legacyMethodName, desc.descriptorString(), newMethodName);
default RenameRuleBuilder methodByClass(final Class<?> owner, final String legacyMethodName, final MethodTypeDesc methodDesc, final String newMethodName) {
return this.method(desc(owner), legacyMethodName, methodDesc, newMethodName);
}

public RenameRuleBuilder methodByInternal(final Iterable<String> owners, final String legacyMethodName, final MethodTypeDesc desc, final String newMethodName) {
for (final String owner : owners) {
this.methodByInternal(owner, legacyMethodName, desc.descriptorString(), newMethodName);
default RenameRuleBuilder method(final Set<ClassDesc> owners, final String legacyMethodName, final MethodTypeDesc methodDesc, final String newMethodName) {
for (final ClassDesc owner : owners) {
this.method(owner, legacyMethodName, methodDesc, newMethodName);
}
return this;
}

public RenameRuleBuilder methodByInternal(final String owner, final String legacyMethodName, final String desc, final String newMethodName) {
this.mappings.put("%s.%s%s".formatted(owner, legacyMethodName, desc), newMethodName);
return this;
}
RenameRuleBuilder method(ClassDesc owner, String legacyMethodName, MethodTypeDesc methodDesc, final String newMethodName);
//</editor-fold>

public RenameRuleBuilder fieldByDesc(final Iterable<ClassDesc> owners, final String legacyFieldName, final String newFieldName) {
for (final ClassDesc owner : owners) {
this.fieldByDesc(owner, legacyFieldName, newFieldName);
//<editor-fold desc="fields" defaultstate="collapsed">
default RenameRuleBuilder fieldByClass(final Set<Class<?>> owners, final String legacyFieldName, final String newFieldName) {
for (final Class<?> owner : owners) {
this.fieldByClass(owner, legacyFieldName, newFieldName);
}
return this;
}

public RenameRuleBuilder fieldByDesc(final ClassDesc owner, final String legacyFieldName, final String newFieldName) {
return this.fieldByInternal(toOwner(owner), legacyFieldName, newFieldName);
default RenameRuleBuilder fieldByClass(final Class<?> owner, final String legacyFieldName, final String newFieldName) {
return this.field(desc(owner), legacyFieldName, newFieldName);
}

public RenameRuleBuilder fieldByInternal(final Iterable<String> owners, final String legacyFieldName, final String newFieldName) {
for (final String owner : owners) {
this.fieldByInternal(owner, legacyFieldName, newFieldName);
default RenameRuleBuilder field(final Set<ClassDesc> owners, final String legacyFieldName, final String newFieldName) {
for (final ClassDesc owner : owners) {
this.field(owner, legacyFieldName, newFieldName);
}
return this;
}

public RenameRuleBuilder fieldByInternal(final String owner, final String legacyFieldName, final String newFieldName) {
this.mappings.put("%s.%s".formatted(owner, legacyFieldName), newFieldName);
return this;
}
RenameRuleBuilder field(ClassDesc owner, String legacyFieldName, String newFieldName);
//</editor-fold>

/**
* Note that you also have to remap the method for the annotation attribute.
*/
public RenameRuleBuilder annotationAttribute(final ClassDesc owner, final String legacyName, final String newName) {
return this.annotationAttribute(owner.descriptorString(), legacyName, newName);
default RenameRuleBuilder annotationAttribute(final Class<?> owner, final String legacyName, final String newName) {
return this.annotationAttribute(desc(owner), legacyName, newName);
}

/**
* Note that you also have to remap the method for the annotation attribute.
*/
public RenameRuleBuilder annotationAttribute(final String ownerDescriptor, final String legacyName, final String newName) {
if (!ownerDescriptor.startsWith("L") || !ownerDescriptor.endsWith(";")) {
throw new IllegalArgumentException("Invalid owner descriptor: %s".formatted(ownerDescriptor));
}
// for some reason the remapper wants the Lpkg/name; format, but just for annotation attributes
this.mappings.put("%s.%s".formatted(ownerDescriptor, legacyName), newName);
return this;
}
RenameRuleBuilder annotationAttribute(ClassDesc owner, String legacyName, String newName);

/**
* Use {@code /} instead of {@code .}.
*/
public RenameRuleBuilder type(final String legacyType, final ClassDesc newType) {
this.mappings.put(legacyType, toOwner(newType));
return this;
default RenameRuleBuilder type(final String legacyType, final Class<?> newType) {
return this.type(legacyType, desc(newType));
}

/**
* Use {@code /} instead of {@code .}.
*/
public RenameRuleBuilder type(final String legacyType, final String newType) {
this.mappings.put(legacyType, newType);
return this;
}
RenameRuleBuilder type(String legacyType, ClassDesc newType);

public RenameRuleBuilder editEnum(final Class<?> enumTypeDesc, final Consumer<EnumRenameBuilder> renamer) {
return this.editEnum(desc(enumTypeDesc), renamer);
default RenameRuleBuilder editEnum(final Class<?> enumType, final Consumer<EnumRenameBuilder> renameBuilder) {
return this.editEnum(desc(enumType), renameBuilder);
}

public RenameRuleBuilder editEnum(final ClassDesc enumTypeDesc, final Consumer<EnumRenameBuilder> renamer) {
final EnumRenameBuilder enumRenamer = new EnumRenameBuilder(enumTypeDesc);
renamer.accept(enumRenamer);
enumRenamer.apply(this);
return this;
}

@Override
public RenameRule build() {
return new RenameRule(this.mappings, this.enumValueOfFieldRenames);
}
RenameRuleBuilder editEnum(ClassDesc enumTypeDesc, Consumer<EnumRenameBuilder> renameBuilder);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package io.papermc.asm.rules.rename;

import java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;

import static io.papermc.asm.util.DescriptorUtils.toOwner;

final class RenameRuleBuilderImpl implements RenameRuleBuilder {

RenameRuleBuilderImpl() {
}

final Map<String, String> mappings = new HashMap<>();
final Map<ClassDesc, EnumRenamer> enumValueOfFieldRenames = new HashMap<>();

@Override
public RenameRuleBuilder method(final ClassDesc owner, final String legacyMethodName, final MethodTypeDesc methodDesc, final String newMethodName) {
this.mappings.put("%s.%s%s".formatted(toOwner(owner), legacyMethodName, methodDesc.descriptorString()), newMethodName);
return this;
}

@Override
public RenameRuleBuilder field(final ClassDesc owner, final String legacyFieldName, final String newFieldName) {
this.mappings.put("%s.%s".formatted(toOwner(owner), legacyFieldName), newFieldName);
return this;
}

@Override
public RenameRuleBuilder annotationAttribute(final ClassDesc owner, final String legacyName, final String newName) {
final String ownerDescriptor = owner.descriptorString();
if (!ownerDescriptor.startsWith("L") || !ownerDescriptor.endsWith(";")) {
throw new IllegalArgumentException("Invalid owner descriptor: %s".formatted(ownerDescriptor));
}
// for some reason the remapper wants the Lpkg/name; format, but just for annotation attributes
this.mappings.put("%s.%s".formatted(ownerDescriptor, legacyName), newName);
return this;
}

@Override
public RenameRuleBuilder type(final String legacyType, final ClassDesc newType) {
this.mappings.put(legacyType, toOwner(newType));
return this;
}

@Override
public RenameRuleBuilder editEnum(final ClassDesc enumTypeDesc, final Consumer<EnumRenameBuilder> renamer) {
final EnumRenameBuilder enumRenamerBuilder = new EnumRenameBuilder(enumTypeDesc);
renamer.accept(enumRenamerBuilder);
final EnumRenamer enumRenamer = enumRenamerBuilder.build();
enumRenamer.fieldRenames().forEach((legacyName, newName) -> {
this.field(enumTypeDesc, legacyName, newName);
});
this.enumValueOfFieldRenames.put(enumTypeDesc, enumRenamer);
return this;
}

@Override
public RenameRule build() {
return new RenameRule(this.mappings, this.enumValueOfFieldRenames);
}
}
Loading

0 comments on commit 51a731b

Please sign in to comment.