From c533d4e8c890c85fe11ddf6025e2d9cbfad60da2 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sun, 22 Jun 2025 15:02:23 -0700 Subject: [PATCH 1/6] Add more asm utils api for future use in Commodore --- .../io/papermc/asm/rules/RuleScanner.java | 78 ++++++++++++++++ .../builder/matcher/method/MethodMatcher.java | 61 +++++++----- .../method/MethodMatcherBuilderImpl.java | 22 ++--- .../matcher/method/MethodMatcherImpl.java | 80 ++++++++++++++++ .../method/MethodParamMatcherBuilder.java | 4 + .../targeted/MethodMatcherPredicate.java | 27 ++++++ .../targeted/TargetedMethodMatcher.java | 4 +- .../TargetedMethodMatcherBuilderImpl.java | 21 ++--- .../targeted/TargetedMethodMatcherImpl.java | 13 +-- .../rules/classes/ClassToInterfaceRule.java | 11 +++ .../rules/classes/EnumToInterfaceRule.java | 9 +- .../asm/rules/method/DirectStaticRewrite.java | 87 ++++++++++++++++++ .../method/OwnableMethodRewriteRule.java | 4 +- .../TargetedTypeGeneratedStaticRewrite.java | 4 +- .../method/params/DirectParameterRewrite.java | 10 +- .../method/params/FuzzyParameterRewrite.java | 12 ++- .../method/params/SuperTypeParamRewrite.java | 14 ++- .../method/returns/DirectReturnRewrite.java | 12 ++- .../method/returns/SubTypeReturnRewrite.java | 14 ++- .../rules/rename/PredicateMethodRemapper.java | 34 +++++++ .../papermc/asm/rules/rename/RenameRule.java | 23 ++++- .../asm/rules/rename/RenameRuleBuilder.java | 37 ++++++++ .../rules/rename/RenameRuleBuilderImpl.java | 22 ++++- .../io/papermc/asm/versioned/ApiVersion.java | 3 - .../versioned/MappedVersionRuleFactory.java | 25 +++-- .../versioned/MergingVersionRuleFactory.java | 28 ++++++ .../OwnedVersionedRuleFactoryFactoryImpl.java | 4 +- .../io/papermc/asm/versioned/Version.java | 13 +++ .../asm/versioned/VersionedRuleScanner.java | 52 +++++++++++ .../io/papermc/asm/rules/TestRuleScanner.java | 66 +++++++++++++ .../asm/rules/rename/RenameRuleTest.java | 8 +- .../versioned/TestVersionedRuleScanner.java | 44 +++++++++ src/testData/java/data/rename/RenameTest.java | 3 + .../java/data/types/rename/TestEnum.java | 6 ++ .../expected/data/rename/RenameTest.class | Bin 2869 -> 2990 bytes .../data/types/rename/RenamedTestEnum.java | 6 ++ 36 files changed, 758 insertions(+), 103 deletions(-) create mode 100644 src/main/java/io/papermc/asm/rules/RuleScanner.java create mode 100644 src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherImpl.java create mode 100644 src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/MethodMatcherPredicate.java create mode 100644 src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java create mode 100644 src/main/java/io/papermc/asm/versioned/MergingVersionRuleFactory.java create mode 100644 src/main/java/io/papermc/asm/versioned/Version.java create mode 100644 src/main/java/io/papermc/asm/versioned/VersionedRuleScanner.java create mode 100644 src/test/java/io/papermc/asm/rules/TestRuleScanner.java create mode 100644 src/test/java/io/papermc/asm/versioned/TestVersionedRuleScanner.java diff --git a/src/main/java/io/papermc/asm/rules/RuleScanner.java b/src/main/java/io/papermc/asm/rules/RuleScanner.java new file mode 100644 index 0000000..7774d7d --- /dev/null +++ b/src/main/java/io/papermc/asm/rules/RuleScanner.java @@ -0,0 +1,78 @@ +package io.papermc.asm.rules; + +import io.papermc.asm.rules.method.DirectStaticRewrite; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.ApiStatus; + +public final class RuleScanner { + + private static final Map, AnnotatedMethodFactory> INTERNAL_FACTORIES = new HashMap<>(); + + static { + register(DirectStaticRewrite.Wrapper.class, DirectStaticRewrite::create); + } + + public static final Map, AnnotatedMethodFactory> FACTORIES = Collections.unmodifiableMap(INTERNAL_FACTORIES); + + private static void register(final Class annotationClass, final AnnotatedMethodFactory factory) { + if (INTERNAL_FACTORIES.containsKey(annotationClass)) { + throw new IllegalArgumentException("Factory for " + annotationClass.getName() + " is already registered"); + } + INTERNAL_FACTORIES.put(annotationClass, factory); + } + + public static RewriteRule scan(final Collection> classes) { + return RewriteRule.chain(classes.stream().map(RuleScanner::scan).toList()); + } + + public static RewriteRule scan(final Class clazz) { + final List rules = new ArrayList<>(); + methods: for (final Method method : clazz.getDeclaredMethods()) { + if (isNotValidMethod(method)) { + continue; + } + // only public static methods + for (final Map.Entry, AnnotatedMethodFactory> entry : FACTORIES.entrySet()) { + final Class annotation = entry.getKey(); + final AnnotatedMethodFactory factory = entry.getValue(); + if (method.isAnnotationPresent(annotation)) { + rules.add(buildRule(method, annotation, factory)); + continue methods; + } + } + } + + return RewriteRule.chain(rules); + } + + public static boolean isNotValidMethod(final Method method) { + if (method.isBridge() || method.isSynthetic()) { + return true; + } + return !Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers()); + } + + @SuppressWarnings("unchecked") + @ApiStatus.Internal + public static RewriteRule buildRule(final Method method, final Class annotationClass, final AnnotatedMethodFactory factory) { + final A instance = Objects.requireNonNull(method.getAnnotation(annotationClass)); + return ((AnnotatedMethodFactory) factory).create(method, instance); + } + + public interface AnnotatedMethodFactory { + + R create(Method method, A annotation); + } + + private RuleScanner() { + } +} diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcher.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcher.java index edffd44..c3e7312 100644 --- a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcher.java +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcher.java @@ -1,35 +1,63 @@ package io.papermc.asm.rules.builder.matcher.method; +import io.papermc.asm.rules.builder.matcher.method.targeted.MethodMatcherPredicate; import java.lang.constant.MethodTypeDesc; +import java.util.Collection; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Predicate; +import org.jetbrains.annotations.Unmodifiable; -@FunctionalInterface -public interface MethodMatcher { +import static io.papermc.asm.util.DescriptorUtils.methodDesc; + +public interface MethodMatcher extends MethodMatcherPredicate { static MethodMatcherBuilder builder() { return new MethodMatcherBuilderImpl(); } + static MethodMatcher falseMatcher() { + return MethodMatcherImpl.FALSE; + } + static MethodMatcher single(final String name, final Consumer matchBuilderConsumer) { return builder().match(name, matchBuilderConsumer).build(); } - boolean matches(int opcode, boolean isInvokeDynamic, String name, String descriptor); + static MethodMatcher create(final Collection methodNames, final Predicate bytecodeDescPredicate) { + return new MethodMatcherImpl(methodNames, bytecodeDescPredicate, (opcode, isInvokeDynamic) -> true); + } + + static MethodMatcher create(final Collection methodNames, final Predicate bytecodeDescPredicate, final OpcodePredicate opcodePredicate) { + return new MethodMatcherImpl(methodNames, bytecodeDescPredicate, opcodePredicate); + } - default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) { - return this.matches(opcode, isInvokeDynamic, name, descriptor.descriptorString()); + default boolean matches(final String name, final String descriptor) { + return this.matches(name, methodDesc(descriptor)); } + boolean matches(String name, MethodTypeDesc descriptor); + + @Override + default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final String descriptor) { + return this.matches(opcode, isInvokeDynamic, name, methodDesc(descriptor)); + } + + boolean matches(int opcode, boolean isInvokeDynamic, String name, MethodTypeDesc descriptor); + + @Unmodifiable Set methodNames(); + + Predicate bytecodeDescPredicate(); + + OpcodePredicate opcodePredicate(); + /** * Creates a method matcher that matches if either matcher passes. * * @param other the other matcher * @return a new "or" matcher */ - default MethodMatcher or(final MethodMatcher other) { - return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) || other.matches(opcode, isInvokeDynamic, name, descriptor); - } + MethodMatcher or(final MethodMatcher other); /** * Creates a method matcher that matches if both matcher pass. @@ -38,9 +66,7 @@ default MethodMatcher or(final MethodMatcher other) { * @return a new "and" matcher * @see #and(Predicate) */ - default MethodMatcher and(final MethodMatcher other) { - return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && other.matches(opcode, isInvokeDynamic, name, descriptor); - } + MethodMatcher and(final MethodMatcher other); /** * Creates a method matcher tha matches if both this matcher @@ -49,16 +75,5 @@ default MethodMatcher and(final MethodMatcher other) { * @param descPredicate the method descriptor predicate * @return a new "and" matcher */ - default MethodMatcher and(final Predicate descPredicate) { - return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && descPredicate.test(MethodTypeDesc.ofDescriptor(descriptor)); - } - - /** - * Creates a method matcher that is the inverse of this matcher. - * - * @return the inverse matcher - */ - default MethodMatcher negate() { - return (opcode, isInvokeDynamic, name, descriptor) -> !this.matches(opcode, isInvokeDynamic, name, descriptor); - } + MethodMatcher and(final Predicate descPredicate); } diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherBuilderImpl.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherBuilderImpl.java index f8e7cc0..7db7d0f 100644 --- a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherBuilderImpl.java +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherBuilderImpl.java @@ -6,12 +6,11 @@ import java.util.Collection; import java.util.Collections; import java.util.Set; -import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Predicate; class MethodMatcherBuilderImpl implements MethodMatcherBuilder { - private MethodMatcher matcher = (o, isInvokeDynamic, n, d) -> false; + private MethodMatcher matcher = MethodMatcher.falseMatcher(); MethodMatcherBuilderImpl() { } @@ -34,8 +33,7 @@ public MethodMatcherBuilder match(final String name, final Consumer names, final Consumer matchBuilderConsumer) { - final Collection namesCopy = Set.copyOf(names); - final SpecificMatchBuilder matchBuilder = new SpecificMatchBuilder(namesCopy::contains); + final SpecificMatchBuilder matchBuilder = new SpecificMatchBuilder(names); matchBuilderConsumer.accept(matchBuilder); matchBuilder.apply(); return this; @@ -49,18 +47,20 @@ public MethodMatcherBuilder desc(final Predicate descPre final class SpecificMatchBuilder implements MatchBuilder { - private final Predicate namePredicate; + private final Collection methodNames; private Predicate bytecodeDescPredicate = $ -> true; - private BiPredicate opcodePredicate = ($, $$) -> true; + private OpcodePredicate opcodePredicate = ($, $$) -> true; - private SpecificMatchBuilder(final Predicate namePredicate) { - this.namePredicate = namePredicate; + private SpecificMatchBuilder(final Collection methodNames) { + this.methodNames = Set.copyOf(methodNames); } private void apply() { - MethodMatcherBuilderImpl.this.matcher = MethodMatcherBuilderImpl.this.matcher.or((o, isInvokeDynamic, n, d) -> { - return this.namePredicate.test(n) && this.opcodePredicate.test(o, isInvokeDynamic) && this.bytecodeDescPredicate.test(MethodTypeDesc.ofDescriptor(d)); - }); + MethodMatcherBuilderImpl.this.matcher = MethodMatcherBuilderImpl.this.matcher.or(MethodMatcher.create( + this.methodNames, + this.bytecodeDescPredicate, + this.opcodePredicate + )); } @Override diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherImpl.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherImpl.java new file mode 100644 index 0000000..7ea3b2c --- /dev/null +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherImpl.java @@ -0,0 +1,80 @@ +package io.papermc.asm.rules.builder.matcher.method; + +import java.lang.constant.MethodTypeDesc; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Predicate; +import org.jetbrains.annotations.Unmodifiable; + +class MethodMatcherImpl implements MethodMatcher { + + static final MethodMatcher FALSE = new MethodMatcherImpl(Collections.emptySet(), $ -> false, ($, $$) -> false); + + private final Set methodNames; + private final Predicate descriptorPredicate; + private final OpcodePredicate opcodePredicate; + + MethodMatcherImpl(final Collection methodNames, final Predicate bytecodeDescPredicate, final OpcodePredicate opcodePredicate) { + this.methodNames = Set.copyOf(methodNames); + this.descriptorPredicate = bytecodeDescPredicate; + this.opcodePredicate = opcodePredicate; + } + + @Override + public boolean matches(final String name, final MethodTypeDesc descriptor) { + return this.methodNames.contains(name) && this.descriptorPredicate.test(descriptor); + } + + @Override + public boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) { + return this.matches(name, descriptor) && this.opcodePredicate.matches(opcode, isInvokeDynamic); // idk which order is faster + } + + @Override + public @Unmodifiable Set methodNames() { + return this.methodNames; + } + + @Override + public Predicate bytecodeDescPredicate() { + return this.descriptorPredicate; + } + + @Override + public OpcodePredicate opcodePredicate() { + return this.opcodePredicate; + } + + @Override + public MethodMatcher or(final MethodMatcher other) { + final Set copy = new HashSet<>(this.methodNames); + copy.addAll(other.methodNames()); + return new MethodMatcherImpl( + copy, + (d) -> this.descriptorPredicate.test( d) || other.bytecodeDescPredicate().test(d), + (o, isInvokeDynamic) -> this.opcodePredicate.matches(o, isInvokeDynamic) || other.opcodePredicate().matches(o, isInvokeDynamic) + ); + } + + @Override + public MethodMatcher and(final MethodMatcher other) { + final Set copy = new HashSet<>(this.methodNames); + copy.retainAll(other.methodNames()); + return new MethodMatcherImpl( + copy, + (d) -> this.descriptorPredicate.test(d) && other.bytecodeDescPredicate().test(d), + (o, isInvokeDynamic) -> this.opcodePredicate.matches(o, isInvokeDynamic) && other.opcodePredicate().matches(o, isInvokeDynamic) + ); + } + + @Override + public MethodMatcher and(final Predicate descPredicate) { + return new MethodMatcherImpl( + this.methodNames, + (d) -> this.descriptorPredicate.test(d) && descPredicate.test(d), + this.opcodePredicate + ); + } +} diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodParamMatcherBuilder.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodParamMatcherBuilder.java index 406d02e..95a4df0 100644 --- a/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodParamMatcherBuilder.java +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodParamMatcherBuilder.java @@ -15,6 +15,10 @@ default B hasParam(final ClassDesc paramClassDesc, final int paramIdx) { return this.desc(d -> d.parameterType(paramIdx).equals(paramClassDesc)); } + default B doesntHaveParam(final ClassDesc paramClassDesc) { + return this.desc(d -> !d.parameterList().contains(paramClassDesc)); + } + default B hasReturn(final ClassDesc returnClassDesc) { return this.desc(d -> d.returnType().equals(returnClassDesc)); } diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/MethodMatcherPredicate.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/MethodMatcherPredicate.java new file mode 100644 index 0000000..8b8b95c --- /dev/null +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/MethodMatcherPredicate.java @@ -0,0 +1,27 @@ +package io.papermc.asm.rules.builder.matcher.method.targeted; + +@FunctionalInterface +public interface MethodMatcherPredicate { + + boolean matches(int opcode, boolean isInvokeDynamic, String name, String descriptor); + + /** + * Creates a method matcher that matches if both this matcher + * and the other matcher pass. + * + * @param other the other matcher + * @return a new "and" matcher + */ + default MethodMatcherPredicate and(final MethodMatcherPredicate other) { + return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && other.matches(opcode, isInvokeDynamic, name, descriptor); + } + + /** + * Creates a new method matcher that is inverted. + * + * @return a new inverted matcher + */ + default MethodMatcherPredicate negate() { + return (opcode, isInvokeDynamic, name, descriptor) -> !this.matches(opcode, isInvokeDynamic, name, descriptor); + } +} diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcher.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcher.java index e6bf476..9e868f8 100644 --- a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcher.java +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcher.java @@ -3,11 +3,13 @@ import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import java.lang.constant.ClassDesc; -public interface TargetedMethodMatcher extends MethodMatcher { +public interface TargetedMethodMatcher { static TargetedMethodMatcherBuilder builder() { return new TargetedMethodMatcherBuilderImpl(); } ClassDesc targetType(); + + MethodMatcher wrapped(); } diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherBuilderImpl.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherBuilderImpl.java index 107c811..e7e0ab7 100644 --- a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherBuilderImpl.java +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherBuilderImpl.java @@ -16,7 +16,7 @@ class TargetedMethodMatcherBuilderImpl implements TargetedMethodMatcherBuilder { - private MethodMatcher matcher = (opcode, isInvokeDynamic, name, descriptor) -> false; + private MethodMatcher matcher = MethodMatcher.falseMatcher(); private Predicate byDesc = $ -> true; private @Nullable ClassDesc oldType; @@ -35,8 +35,7 @@ public TargetedMethodMatcherBuilder match(final String name, final Consumer names, final Consumer matchBuilderConsumer) { - final Collection namesCopy = Set.copyOf(names); - final SpecificMatchBuilder matchBuilder = new SpecificMatchBuilder(namesCopy::contains); + final SpecificMatchBuilder matchBuilder = new SpecificMatchBuilder(names); matchBuilderConsumer.accept(matchBuilder); matchBuilder.apply(); return this; @@ -77,20 +76,20 @@ public TargetedMethodMatcher build() { final class SpecificMatchBuilder implements TargetedMethodMatcherBuilder.MatchBuilder { - private final Predicate namePredicate; + private final Collection methodNames; private Predicate bytecodeDescPredicate = $ -> true; private OpcodePredicate opcodePredicate = ($, $$) -> true; - private SpecificMatchBuilder(final Predicate namePredicate) { - this.namePredicate = namePredicate; + private SpecificMatchBuilder(final Collection methodNames) { + this.methodNames = Set.copyOf(methodNames); } private void apply() { - TargetedMethodMatcherBuilderImpl.this.matcher = TargetedMethodMatcherBuilderImpl.this.matcher.or((o, isInvokeDynamic, n, d) -> { - return this.namePredicate.test(n) - && this.opcodePredicate.matches(o, isInvokeDynamic) - && this.bytecodeDescPredicate.test(MethodTypeDesc.ofDescriptor(d)); - }); + TargetedMethodMatcherBuilderImpl.this.matcher = TargetedMethodMatcherBuilderImpl.this.matcher.or(MethodMatcher.create( + this.methodNames, + this.bytecodeDescPredicate, + this.opcodePredicate + )); } @Override diff --git a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherImpl.java b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherImpl.java index ff0cab7..230f1ca 100644 --- a/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherImpl.java +++ b/src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcherImpl.java @@ -14,17 +14,12 @@ public class TargetedMethodMatcherImpl implements TargetedMethodMatcher { } @Override - public boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final String descriptor) { - return this.wrapped.matches(opcode, isInvokeDynamic, name, descriptor); - } - - @Override - public MethodMatcher negate() { - return new TargetedMethodMatcherImpl(this.wrapped.negate(), this.oldType); + public ClassDesc targetType() { + return this.oldType; } @Override - public ClassDesc targetType() { - return this.oldType; + public MethodMatcher wrapped() { + return this.wrapped; } } diff --git a/src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java b/src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java index 35ed512..c21571b 100644 --- a/src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java +++ b/src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java @@ -8,6 +8,7 @@ import io.papermc.asm.rules.method.rewrite.SimpleRewrite; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; +import java.util.Optional; import java.util.Set; import org.jspecify.annotations.Nullable; import org.objectweb.asm.ClassVisitor; @@ -25,6 +26,16 @@ public final class ClassToInterfaceRule implements RewriteRule.Delegate { private final @Nullable ClassDesc redirectExtension; private final RewriteRule rule; + /** + * Rewrites all methods on a target class to be interface methods. + * + * @param owner the class to target + * @param redirectExtension an optional new owner for the methods to maintain more compatibility if the bytecode extended the owner type + */ + public ClassToInterfaceRule(final Class owner, final @Nullable Class redirectExtension) { + this(owner.describeConstable().orElseThrow(), Optional.ofNullable(redirectExtension).flatMap(Class::describeConstable).orElse(null)); + } + /** * Rewrites all methods on a target class to be interface methods. * diff --git a/src/main/java/io/papermc/asm/rules/classes/EnumToInterfaceRule.java b/src/main/java/io/papermc/asm/rules/classes/EnumToInterfaceRule.java index 7d4a10a..0df6a95 100644 --- a/src/main/java/io/papermc/asm/rules/classes/EnumToInterfaceRule.java +++ b/src/main/java/io/papermc/asm/rules/classes/EnumToInterfaceRule.java @@ -3,6 +3,7 @@ import io.papermc.asm.ClassProcessingContext; import io.papermc.asm.rules.RewriteRule; import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import io.papermc.asm.rules.builder.matcher.method.targeted.MethodMatcherPredicate; import io.papermc.asm.rules.method.DirectStaticRewrite; import io.papermc.asm.rules.method.OwnableMethodRewriteRule; import io.papermc.asm.rules.method.generated.GeneratedStaticRewrite; @@ -36,19 +37,19 @@ public class EnumToInterfaceRule implements RewriteRule.Delegate { .match("getDeclaringClass", b -> b.virtual().desc(MethodTypeDesc.of(ConstantDescs.CD_Class))) .match("describeConstable", b -> b.virtual().desc(MethodTypeDesc.of(desc(Optional.class)))) .build(); - private static final MethodMatcher NOT_ENUM_METHODS_BASE = ENUM_VIRTUAL_METHODS.negate(); + private static final MethodMatcherPredicate NOT_ENUM_METHODS_BASE = ENUM_VIRTUAL_METHODS.negate(); private static final ClassDesc LEGACY_ENUM = desc(LegacyEnum.class); private final Map enums; private final RewriteRule rule; - private final MethodMatcher notEnumMethods; + private final MethodMatcherPredicate notEnumMethods; public EnumToInterfaceRule(final Map enums) { this.enums = enums; final List rules = new ArrayList<>(); rules.add(new EnumVirtualMethods()); - MethodMatcher notEnums = NOT_ENUM_METHODS_BASE; + MethodMatcherPredicate notEnums = NOT_ENUM_METHODS_BASE; for (final Map.Entry entry : enums.entrySet()) { final MethodMatcher matcher = createStaticMatcher(entry.getKey()); notEnums = notEnums.and(matcher.negate()); @@ -115,7 +116,7 @@ public void generateConstructor(final GeneratorAdapterFactory factory, final Met final class NotEnumMethods implements OwnableMethodRewriteRule.Filtered { @Override - public MethodMatcher methodMatcher() { + public MethodMatcherPredicate methodMatcher() { return EnumToInterfaceRule.this.notEnumMethods; } diff --git a/src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java b/src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java index 1d38a79..cd9f11a 100644 --- a/src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java @@ -3,18 +3,29 @@ import io.papermc.asm.ClassProcessingContext; import io.papermc.asm.rules.RewriteRule; import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import io.papermc.asm.rules.builder.matcher.method.MethodType; import io.papermc.asm.rules.generate.GeneratedMethodHolder; +import io.papermc.asm.rules.method.params.DirectParameterRewrite; import io.papermc.asm.rules.method.rewrite.ConstructorRewrite; import io.papermc.asm.rules.method.rewrite.MethodRewrite; import io.papermc.asm.rules.method.rewrite.SimpleRewrite; +import io.papermc.asm.util.DescriptorUtils; import io.papermc.asm.versioned.ApiVersion; import io.papermc.asm.versioned.VersionedRuleFactory; import io.papermc.asm.versioned.matcher.VersionedMatcher; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; +import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Set; +import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.Nullable; +import static io.papermc.asm.util.DescriptorUtils.desc; import static io.papermc.asm.util.DescriptorUtils.toOwner; import static io.papermc.asm.util.OpcodeUtils.staticOp; @@ -61,4 +72,80 @@ public RewriteRule createRule(final ApiVersion apiVersion) { return this.versions.ruleForVersion(apiVersion, match -> new DirectStaticRewrite(this.owners(), this.staticMethodName(), match, this.staticRedirectOwner())); } } + + /** + * Annotate a {@code public} {@code static} method with this annotation and then scan it with TODO + * to automatically generate a {@link DirectParameterRewrite} rule. + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Wrapper { + + String IMPLIED_METHOD_NAME = "__%%implied_method_name%%__"; + + /** + * The owners of the method to rewrite. + * + * @return the owners + */ + String[] owners() default {}; + + /** + * The owners of the method to rewrite. + * + * @return the owners + */ + Class[] ownerClasses() default {}; + + /** + * The type of the method to rewrite. + * + * @return the type + */ + MethodType type() default MethodType.VIRTUAL; + + /** + * The type to convert the parameters to. + * + * @return the type to convert to + */ + String methodName() default IMPLIED_METHOD_NAME; + + } + + @ApiStatus.Internal + public static DirectStaticRewrite create(final Method method, final Wrapper annotation) { + final ClassDesc[] owners = new ClassDesc[annotation.owners().length + annotation.ownerClasses().length]; + if (owners.length < 1) { + throw new IllegalArgumentException("Method " + method.getName() + " must have at least one owner"); + } + for (int i = 0; i < annotation.owners().length; i++) { + owners[i] = ClassDesc.of(annotation.owners()[i]); + } + for (int i = 0; i < annotation.ownerClasses().length; i++) { + owners[i + annotation.owners().length] = annotation.ownerClasses()[i].describeConstable().orElseThrow(); + } + final String targetMethodName = annotation.methodName().equals(Wrapper.IMPLIED_METHOD_NAME) ? method.getName() : annotation.methodName(); + final Class[] parameterTypes; + if (annotation.type() == MethodType.VIRTUAL || annotation.type() == MethodType.INTERFACE) { + if (method.getParameterTypes().length < 1) { + throw new IllegalArgumentException("Method " + targetMethodName + " requires at least one parameter"); + } + parameterTypes = new Class[method.getParameterTypes().length - 1]; + System.arraycopy(method.getParameterTypes(), 1, parameterTypes, 0, parameterTypes.length); + } else { + parameterTypes = method.getParameterTypes(); + } + final ClassDesc[] parameterDescs = Arrays.stream(parameterTypes).map(DescriptorUtils::desc).toArray(ClassDesc[]::new); + final MethodTypeDesc methodDesc = MethodTypeDesc.of(desc(method.getReturnType()), parameterDescs); + final MethodMatcher matcher = MethodMatcher.builder() + .match(targetMethodName, b -> b.type(annotation.type()).desc(methodDesc)) + .build(); + return new DirectStaticRewrite( + Set.of(owners), + method.getName(), + matcher, + desc(method.getDeclaringClass()) + ); + } } diff --git a/src/main/java/io/papermc/asm/rules/method/OwnableMethodRewriteRule.java b/src/main/java/io/papermc/asm/rules/method/OwnableMethodRewriteRule.java index f0ac6db..ef94441 100644 --- a/src/main/java/io/papermc/asm/rules/method/OwnableMethodRewriteRule.java +++ b/src/main/java/io/papermc/asm/rules/method/OwnableMethodRewriteRule.java @@ -2,7 +2,7 @@ import io.papermc.asm.ClassProcessingContext; import io.papermc.asm.rules.OwnableRewriteRule; -import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import io.papermc.asm.rules.builder.matcher.method.targeted.MethodMatcherPredicate; /** * A rule that targets specific owners. @@ -24,7 +24,7 @@ interface Filtered extends OwnableMethodRewriteRule { * * @return the method matcher */ - MethodMatcher methodMatcher(); + MethodMatcherPredicate methodMatcher(); @Override default boolean shouldProcess(final ClassProcessingContext context, final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface, final boolean isInvokeDynamic) { diff --git a/src/main/java/io/papermc/asm/rules/method/generated/TargetedTypeGeneratedStaticRewrite.java b/src/main/java/io/papermc/asm/rules/method/generated/TargetedTypeGeneratedStaticRewrite.java index f0c80a5..c3eea29 100644 --- a/src/main/java/io/papermc/asm/rules/method/generated/TargetedTypeGeneratedStaticRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/generated/TargetedTypeGeneratedStaticRewrite.java @@ -25,13 +25,13 @@ interface Parameter extends TargetedTypeGeneratedStaticRewrite, StaticHandlerGen * * @return the targeted method matcher */ - TargetedMethodMatcher methodMatcher(); + TargetedMethodMatcher targetedMethodMatcher(); @Override default MethodTypeDesc transformInvokedDescriptor(final MethodTypeDesc original, final Set context) { // To create the generated method descriptor from the existing (in source) descriptor, we replace the // legacy param with the new param - return replaceParameters(original, isEqual(this.methodMatcher().targetType()), this.existingType(), context); + return replaceParameters(original, isEqual(this.targetedMethodMatcher().targetType()), this.existingType(), context); } } diff --git a/src/main/java/io/papermc/asm/rules/method/params/DirectParameterRewrite.java b/src/main/java/io/papermc/asm/rules/method/params/DirectParameterRewrite.java index 560aa85..1c42903 100644 --- a/src/main/java/io/papermc/asm/rules/method/params/DirectParameterRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/params/DirectParameterRewrite.java @@ -1,6 +1,7 @@ package io.papermc.asm.rules.method.params; import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; import io.papermc.asm.rules.method.OwnableMethodRewriteRule; import io.papermc.asm.rules.method.generated.TargetedTypeGeneratedStaticRewrite; @@ -18,10 +19,15 @@ * * @param owners the owners to target * @param existingType the type to convert to - * @param methodMatcher the method matcher to use which targets the legacy param type + * @param targetedMethodMatcher the method matcher to use which targets the legacy param type * @param staticHandler the method which will be used to convert the legacy type to the new type */ -public record DirectParameterRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher methodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter, OwnableMethodRewriteRule.Filtered { +public record DirectParameterRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher targetedMethodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter, OwnableMethodRewriteRule.Filtered { + + @Override + public MethodMatcher methodMatcher() { + return this.targetedMethodMatcher.wrapped(); + } public record Versioned(Set owners, ClassDesc existingType, VersionedMatcher versions) implements VersionedRuleFactory { diff --git a/src/main/java/io/papermc/asm/rules/method/params/FuzzyParameterRewrite.java b/src/main/java/io/papermc/asm/rules/method/params/FuzzyParameterRewrite.java index 72d8319..64fa0a0 100644 --- a/src/main/java/io/papermc/asm/rules/method/params/FuzzyParameterRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/params/FuzzyParameterRewrite.java @@ -2,6 +2,7 @@ import io.papermc.asm.ClassProcessingContext; import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; import io.papermc.asm.rules.method.OwnableMethodRewriteRule; import io.papermc.asm.rules.method.generated.TargetedTypeGeneratedStaticRewrite; @@ -27,15 +28,20 @@ * * @param owners the owners to target * @param existingType the type to convert to - * @param methodMatcher the method matcher to use which targets the legacy param type + * @param targetedMethodMatcher the method matcher to use which targets the legacy param type * @param staticHandler the method which will be used to convert the legacy type to the new type */ -public record FuzzyParameterRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher methodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter, OwnableMethodRewriteRule.Filtered { +public record FuzzyParameterRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher targetedMethodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter, OwnableMethodRewriteRule.Filtered { + + @Override + public MethodMatcher methodMatcher() { + return this.targetedMethodMatcher().wrapped(); + } @Override public MethodTypeDesc transformToRedirectDescriptor(final MethodTypeDesc intermediateDescriptor) { // We need to replace the parameters in the bytecode descriptor that match the target with the fuzzy param - return replaceParameters(intermediateDescriptor, isEqual(this.methodMatcher().targetType()), ConstantDescs.CD_Object); + return replaceParameters(intermediateDescriptor, isEqual(this.targetedMethodMatcher().targetType()), ConstantDescs.CD_Object); } @Override diff --git a/src/main/java/io/papermc/asm/rules/method/params/SuperTypeParamRewrite.java b/src/main/java/io/papermc/asm/rules/method/params/SuperTypeParamRewrite.java index a11523f..14c290d 100644 --- a/src/main/java/io/papermc/asm/rules/method/params/SuperTypeParamRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/params/SuperTypeParamRewrite.java @@ -2,6 +2,7 @@ import io.papermc.asm.ClassProcessingContext; import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; import io.papermc.asm.rules.method.OwnableMethodRewriteRule; import io.papermc.asm.rules.method.rewrite.MethodRewrite; @@ -21,13 +22,18 @@ * offending parameter in the descriptor and move on. * * @param owners owners of the methods to change - * @param methodMatcher method matcher to find methods with (target is the type to be found in bytecode that needs to be transformed) + * @param targetedMethodMatcher method matcher to find methods with (target is the type to be found in bytecode that needs to be transformed) * @param newParamType the parameter type that is valid for existing method */ -public record SuperTypeParamRewrite(Set owners, TargetedMethodMatcher methodMatcher, ClassDesc newParamType) implements OwnableMethodRewriteRule.Filtered { +public record SuperTypeParamRewrite(Set owners, TargetedMethodMatcher targetedMethodMatcher, ClassDesc newParamType) implements OwnableMethodRewriteRule.Filtered { + + @Override + public MethodMatcher methodMatcher() { + return this.targetedMethodMatcher.wrapped(); + } public ClassDesc oldParamType() { - return this.methodMatcher.targetType(); + return this.targetedMethodMatcher.targetType(); } @Override @@ -36,7 +42,7 @@ public MethodRewrite rewrite(final ClassProcessingContext context, final bool } private MethodTypeDesc modifyMethodDescriptor(final MethodTypeDesc methodDescriptor) { - return replaceParameters(methodDescriptor, isEqual(this.methodMatcher().targetType()), this.newParamType()); + return replaceParameters(methodDescriptor, isEqual(this.targetedMethodMatcher.targetType()), this.newParamType()); } public record Versioned(Set owners, ClassDesc newParamType, VersionedMatcher versions) implements VersionedRuleFactory { diff --git a/src/main/java/io/papermc/asm/rules/method/returns/DirectReturnRewrite.java b/src/main/java/io/papermc/asm/rules/method/returns/DirectReturnRewrite.java index 61284e8..bcb0f23 100644 --- a/src/main/java/io/papermc/asm/rules/method/returns/DirectReturnRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/returns/DirectReturnRewrite.java @@ -1,6 +1,7 @@ package io.papermc.asm.rules.method.returns; import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; import io.papermc.asm.rules.method.OwnableMethodRewriteRule; import io.papermc.asm.rules.method.generated.TargetedTypeGeneratedStaticRewrite; @@ -17,25 +18,30 @@ // Uses the methodMatcher against bytecode from plugins. Any matching descriptors will have the method name/owner changed to point towards // a generated method of the same descriptor. That generated method will call the original method and pass the return value // to staticHandler. staticHandler will then convert the object to the plugin bytecode's expected type. -public record DirectReturnRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher methodMatcher, Method staticHandler, boolean includeOwnerContext) implements TargetedTypeGeneratedStaticRewrite.Return, OwnableMethodRewriteRule.Filtered { +public record DirectReturnRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher targetedMethodMatcher, Method staticHandler, boolean includeOwnerContext) implements TargetedTypeGeneratedStaticRewrite.Return, OwnableMethodRewriteRule.Filtered { public DirectReturnRewrite { if (includeOwnerContext && owners.size() > 1) { throw new IllegalArgumentException("Can't include owner context with multiple owners"); } final ClassDesc owner = owners.iterator().next(); - if (!desc(staticHandler.getReturnType()).equals(methodMatcher.targetType())) { + if (!desc(staticHandler.getReturnType()).equals(targetedMethodMatcher.targetType())) { throw new IllegalArgumentException("Return type of staticHandler doesn't match target from methodMatcher"); } final int expectedStaticHandlerParamCount = includeOwnerContext ? 2 : 1; if (staticHandler.getParameterCount() != expectedStaticHandlerParamCount) { - throw new IllegalArgumentException("staticHandler should only have %s parameter(s) of type %s".formatted(expectedStaticHandlerParamCount, (includeOwnerContext ? owner + " and " : "") + methodMatcher.targetType())); + throw new IllegalArgumentException("staticHandler should only have %s parameter(s) of type %s".formatted(expectedStaticHandlerParamCount, (includeOwnerContext ? owner + " and " : "") + targetedMethodMatcher.targetType())); } if (!staticHandler.getParameterTypes()[includeOwnerContext ? 1 : 0].describeConstable().orElseThrow().equals(existingType)) { throw new IllegalArgumentException("staticHandler param type isn't " + existingType); } } + @Override + public MethodMatcher methodMatcher() { + return this.targetedMethodMatcher.wrapped(); + } + public record Versioned(Set owners, ClassDesc existingType, VersionedMatcher versions, boolean includeOwnerContext) implements VersionedRuleFactory { @Override diff --git a/src/main/java/io/papermc/asm/rules/method/returns/SubTypeReturnRewrite.java b/src/main/java/io/papermc/asm/rules/method/returns/SubTypeReturnRewrite.java index ee4e94f..f64f614 100644 --- a/src/main/java/io/papermc/asm/rules/method/returns/SubTypeReturnRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/returns/SubTypeReturnRewrite.java @@ -2,6 +2,7 @@ import io.papermc.asm.ClassProcessingContext; import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; import io.papermc.asm.rules.method.OwnableMethodRewriteRule; import io.papermc.asm.rules.method.rewrite.MethodRewrite; @@ -19,18 +20,23 @@ * We just change the return type in the descriptor and move on. * * @param owners owners of the methods to change - * @param methodMatcher method matcher to find methods with (target is the type to be found in bytecode that needs to be transformed) + * @param targetedMethodMatcher method matcher to find methods with (target is the type to be found in bytecode that needs to be transformed) * @param newReturnType the return type that is valid for existing method */ -public record SubTypeReturnRewrite(Set owners, TargetedMethodMatcher methodMatcher, ClassDesc newReturnType) implements OwnableMethodRewriteRule.Filtered { +public record SubTypeReturnRewrite(Set owners, TargetedMethodMatcher targetedMethodMatcher, ClassDesc newReturnType) implements OwnableMethodRewriteRule.Filtered { + + @Override + public MethodMatcher methodMatcher() { + return this.targetedMethodMatcher.wrapped(); + } public ClassDesc oldReturnType() { - return this.methodMatcher.targetType(); + return this.targetedMethodMatcher.targetType(); } @Override public @Nullable MethodRewrite rewrite(final ClassProcessingContext context, final boolean isInvokeDynamic, final int opcode, final ClassDesc owner, final String name, final MethodTypeDesc descriptor, final boolean isInterface) { - if (descriptor.returnType().equals(this.methodMatcher().targetType())) { + if (descriptor.returnType().equals(this.targetedMethodMatcher().targetType())) { return new SimpleRewrite(opcode, owner, name, this.modifyMethodDescriptor(descriptor), isInterface, isInvokeDynamic); } return null; diff --git a/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java b/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java new file mode 100644 index 0000000..b99330f --- /dev/null +++ b/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java @@ -0,0 +1,34 @@ +package io.papermc.asm.rules.rename; + +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import java.util.List; +import java.util.Map; +import java.util.function.UnaryOperator; +import org.objectweb.asm.commons.Remapper; + +public class PredicateMethodRemapper extends Remapper { + + private final Map>> methodRemaps; + + public PredicateMethodRemapper(final Map>> methodRemaps) { + this.methodRemaps = methodRemaps; + } + + @Override + public String mapMethodName(final String owner, final String name, final String descriptor) { + final Map> ownerRemaps = this.methodRemaps.get(owner); + if (ownerRemaps != null) { + final List methodRemaps = ownerRemaps.get(name); + if (methodRemaps != null) { + for (final MatcherPair remap : methodRemaps) { + if (remap.matcher().matches(name, descriptor)) { + return remap.newName.apply(name); + } + } + } + } + return super.mapMethodName(owner, name, descriptor); + } + + public record MatcherPair(MethodMatcher matcher, UnaryOperator newName) {} +} diff --git a/src/main/java/io/papermc/asm/rules/rename/RenameRule.java b/src/main/java/io/papermc/asm/rules/rename/RenameRule.java index 6073963..3175d00 100644 --- a/src/main/java/io/papermc/asm/rules/rename/RenameRule.java +++ b/src/main/java/io/papermc/asm/rules/rename/RenameRule.java @@ -20,11 +20,13 @@ public static RenameRuleBuilder builder() { private final Map renames; private final Map enumFieldRenames; + private final Map>> predicateMethodRemaps; private @Nullable RewriteRule rule; - public RenameRule(final Map renames, final Map enumFieldRenames) { + public RenameRule(final Map renames, final Map enumFieldRenames, final Map>> predicateMethodRemaps) { this.renames = Map.copyOf(renames); this.enumFieldRenames = Map.copyOf(enumFieldRenames); + this.predicateMethodRemaps = Map.copyOf(predicateMethodRemaps); } public Map renames() { @@ -39,11 +41,13 @@ public Map enumFieldRenames() { public RewriteRule delegate() { if (this.rule == null) { final Remapper remapper = new SimpleRemapper(Map.copyOf(this.renames)); + final Remapper predicateRemapper = new PredicateMethodRemapper(this.predicateMethodRemaps); final List 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, predicateRemapper)); rules.add((api, parent, context) -> new FixedClassRemapper(api, parent, remapper)); this.rule = RewriteRule.chain(rules); @@ -54,11 +58,24 @@ public RewriteRule delegate() { @Override public RenameRule merge(final RenameRule other) { final Map regularRenames = new HashMap<>(this.renames); - final Map enumFieldRenames = new HashMap<>(this.enumFieldRenames); regularRenames.putAll(other.renames); + + final Map enumFieldRenames = new HashMap<>(this.enumFieldRenames); other.enumFieldRenames.forEach((cd, renamer) -> { enumFieldRenames.merge(cd, renamer, EnumRenamer::overwrite); }); - return new RenameRule(regularRenames, enumFieldRenames); + + final Map>> newPredicateRenames = new HashMap<>(this.predicateMethodRemaps); + other.predicateMethodRemaps.forEach((s, methods) -> { + final Map> newMethods = new HashMap<>(newPredicateRenames.computeIfAbsent(s, k -> new HashMap<>())); + methods.forEach((name, pairs) -> { + final List newPairs = new ArrayList<>(newMethods.computeIfAbsent(name, k -> new ArrayList<>())); + newPairs.addAll(pairs); + newMethods.put(name, List.copyOf(newPairs)); + }); + newPredicateRenames.put(s, Map.copyOf(newMethods)); + }); + + return new RenameRule(regularRenames, enumFieldRenames, newPredicateRenames); } } diff --git a/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilder.java b/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilder.java index 3db6c97..47325e9 100644 --- a/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilder.java +++ b/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilder.java @@ -1,10 +1,13 @@ package io.papermc.asm.rules.rename; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.util.Builder; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; +import java.util.Map; import java.util.Set; import java.util.function.Consumer; +import java.util.function.UnaryOperator; import static io.papermc.asm.util.DescriptorUtils.desc; @@ -32,7 +35,20 @@ default RenameRuleBuilder method(final Set owners, final String legac RenameRuleBuilder method(ClassDesc owner, String legacyMethodName, MethodTypeDesc methodDesc, final String newMethodName); // + default RenameRuleBuilder methodPredicate(final Class owner, final MethodMatcher matcher, final UnaryOperator newMethodName) { + return this.methodPredicate(desc(owner), matcher, newMethodName); + } + + RenameRuleBuilder methodPredicate(ClassDesc owner, final MethodMatcher matcher, final UnaryOperator newMethodName); + // + default RenameRuleBuilder fieldsByClass(final Set> owners, final Map legacyToNewFieldNames) { + legacyToNewFieldNames.forEach((legacyName, newName) -> { + this.fieldByClass(owners, legacyName, newName); + }); + return this; + } + default RenameRuleBuilder fieldByClass(final Set> owners, final String legacyFieldName, final String newFieldName) { for (final Class owner : owners) { this.fieldByClass(owner, legacyFieldName, newFieldName); @@ -40,10 +56,24 @@ default RenameRuleBuilder fieldByClass(final Set> owners, final String return this; } + default RenameRuleBuilder fieldsByClass(final Class owner, final Map legacyToNewFieldNames) { + legacyToNewFieldNames.forEach((legacyName, newName) -> { + this.fieldByClass(owner, legacyName, newName); + }); + return this; + } + default RenameRuleBuilder fieldByClass(final Class owner, final String legacyFieldName, final String newFieldName) { return this.field(desc(owner), legacyFieldName, newFieldName); } + default RenameRuleBuilder fields(final Set owners, final Map legacyToNewFieldNames) { + legacyToNewFieldNames.forEach((legacyName, newName) -> { + this.field(owners, legacyName, newName); + }); + return this; + } + default RenameRuleBuilder field(final Set owners, final String legacyFieldName, final String newFieldName) { for (final ClassDesc owner : owners) { this.field(owner, legacyFieldName, newFieldName); @@ -51,6 +81,13 @@ default RenameRuleBuilder field(final Set owners, final String legacy return this; } + default RenameRuleBuilder fields(final ClassDesc owner, final Map legacyToNewFieldNames) { + legacyToNewFieldNames.forEach((legacyName, newName) -> { + this.field(owner, legacyName, newName); + }); + return this; + } + RenameRuleBuilder field(ClassDesc owner, String legacyFieldName, String newFieldName); // diff --git a/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java b/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java index 2cdafed..ed97b79 100644 --- a/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java +++ b/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java @@ -1,10 +1,14 @@ package io.papermc.asm.rules.rename; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Consumer; +import java.util.function.UnaryOperator; import static io.papermc.asm.util.DescriptorUtils.toOwner; @@ -15,6 +19,7 @@ final class RenameRuleBuilderImpl implements RenameRuleBuilder { final Map mappings = new HashMap<>(); final Map enumValueOfFieldRenames = new HashMap<>(); + final Map>> predicateMethodRenames = new HashMap<>(); @Override public RenameRuleBuilder method(final ClassDesc owner, final String legacyMethodName, final MethodTypeDesc methodDesc, final String newMethodName) { @@ -22,6 +27,21 @@ public RenameRuleBuilder method(final ClassDesc owner, final String legacyMethod return this; } + @Override + public RenameRuleBuilder methodPredicate(final ClassDesc owner, final MethodMatcher matcher, final UnaryOperator newMethodName) { + if (matcher.methodNames().isEmpty()) { + throw new IllegalArgumentException("Method matcher must have at least one method name"); + } + final Map> ownerPredicateRenames = this.predicateMethodRenames.computeIfAbsent(toOwner(owner), k -> new HashMap<>()); + for (final String methodName : matcher.methodNames()) { + final List matcherPairs = ownerPredicateRenames.computeIfAbsent(methodName, k -> new ArrayList<>()); + matcherPairs.add(new PredicateMethodRemapper.MatcherPair(matcher, newMethodName)); + ownerPredicateRenames.put(methodName, List.copyOf(matcherPairs)); + } + this.predicateMethodRenames.put(toOwner(owner), Map.copyOf(ownerPredicateRenames)); + 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); @@ -59,6 +79,6 @@ public RenameRuleBuilder editEnum(final ClassDesc enumTypeDesc, final Consumer> extends Comparable { default boolean isNewerThan(final I apiVersion) { diff --git a/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java b/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java index 400bafb..044330f 100644 --- a/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java +++ b/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java @@ -2,26 +2,25 @@ import io.papermc.asm.rules.RewriteRule; 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 java.util.function.BinaryOperator; +import java.util.TreeMap; -public record MappedVersionRuleFactory(NavigableMap, ? extends R> versions, BinaryOperator mergeFunction) implements VersionedRuleFactory { +public record MappedVersionRuleFactory(NavigableMap, RewriteRule> versions) implements VersionedRuleFactory { - public static > MappedVersionRuleFactory mergeable(final NavigableMap, M> versions) { - return new MappedVersionRuleFactory<>(versions, Mergeable::merge); + public static MappedVersionRuleFactory create(final Map, List> rules) { + final Map, RewriteRule> map = new HashMap<>(); + rules.forEach((apiVersion, rewriteRules) -> { + map.put(apiVersion, RewriteRule.chain(rewriteRules)); + }); + return new MappedVersionRuleFactory(new TreeMap<>(map)); } @Override public RewriteRule createRule(final ApiVersion apiVersion) { - final List 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); - return toMerge.stream().reduce(this.mergeFunction).orElseThrow(); + final List toMerge = new ArrayList<>(this.versions.tailMap(apiVersion, true).values()); + return RewriteRule.chain(toMerge); } } diff --git a/src/main/java/io/papermc/asm/versioned/MergingVersionRuleFactory.java b/src/main/java/io/papermc/asm/versioned/MergingVersionRuleFactory.java new file mode 100644 index 0000000..820acb9 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/MergingVersionRuleFactory.java @@ -0,0 +1,28 @@ +package io.papermc.asm.versioned; + +import io.papermc.asm.rules.RewriteRule; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.function.BinaryOperator; + +public record MergingVersionRuleFactory(NavigableMap, ? extends R> versions, BinaryOperator mergeFunction) implements VersionedRuleFactory { + + public static > MergingVersionRuleFactory mergeable(final NavigableMap, M> versions) { + return new MergingVersionRuleFactory<>(new TreeMap, M>(versions), Mergeable::merge); + } + + @Override + public RewriteRule createRule(final ApiVersion apiVersion) { + final List 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); + return toMerge.stream().reduce(this.mergeFunction).orElseThrow(); + } +} diff --git a/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java b/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java index 9e09a13..0376806 100644 --- a/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java +++ b/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java @@ -77,12 +77,12 @@ public void moveInstanceMethod(final ClassDesc newOwner, final String newMethodN @Override public > void addMergeableRuleFactory(final NavigableMap, R> versions) { - this.factories.add(new MappedVersionRuleFactory<>(versions, Mergeable::merge)); + this.factories.add(new MergingVersionRuleFactory<>(versions, Mergeable::merge)); } @Override public void addChainableRuleFactory(final NavigableMap, ? extends RewriteRule> versions) { - this.factories.add(new MappedVersionRuleFactory(versions, RewriteRule::chain)); + this.factories.add(new MergingVersionRuleFactory(versions, RewriteRule::chain)); } @Override diff --git a/src/main/java/io/papermc/asm/versioned/Version.java b/src/main/java/io/papermc/asm/versioned/Version.java new file mode 100644 index 0000000..f4cb295 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/Version.java @@ -0,0 +1,13 @@ +package io.papermc.asm.versioned; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Version { + + String value(); +} diff --git a/src/main/java/io/papermc/asm/versioned/VersionedRuleScanner.java b/src/main/java/io/papermc/asm/versioned/VersionedRuleScanner.java new file mode 100644 index 0000000..a3c93f5 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/VersionedRuleScanner.java @@ -0,0 +1,52 @@ +package io.papermc.asm.versioned; + +import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.RuleScanner; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +public final class VersionedRuleScanner { + + private final Function> versionFactory; + + public VersionedRuleScanner(final Function> versionFactory) { + this.versionFactory = versionFactory; + } + + public VersionedRuleFactory scan(final Collection> classes) { + return VersionedRuleFactory.chain(classes.stream().map(this::scan).toList()); + } + + public VersionedRuleFactory scan(final Class clazz) { + final Map, List> versions = new HashMap<>(); + + methods: for (final Method method : clazz.getDeclaredMethods()) { + if (RuleScanner.isNotValidMethod(method)) { + continue; + } + // only public static methods + for (final Map.Entry, RuleScanner.AnnotatedMethodFactory> entry : RuleScanner.FACTORIES.entrySet()) { + final Class annotation = entry.getKey(); + final RuleScanner.AnnotatedMethodFactory factory = entry.getValue(); + if (method.isAnnotationPresent(annotation)) { + if (!method.isAnnotationPresent(Version.class)) { + throw new IllegalArgumentException("Method " + method + " annotated with " + annotation + " is not annotated with @Version"); + } + //noinspection DataFlowIssue + final ApiVersion version = this.versionFactory.apply(method.getAnnotation(Version.class).value()); + versions.computeIfAbsent(version, k -> new ArrayList<>()).add(RuleScanner.buildRule(method, annotation, factory)); + continue methods; + } + } + } + + return MappedVersionRuleFactory.create(versions); + } + +} diff --git a/src/test/java/io/papermc/asm/rules/TestRuleScanner.java b/src/test/java/io/papermc/asm/rules/TestRuleScanner.java new file mode 100644 index 0000000..6821f93 --- /dev/null +++ b/src/test/java/io/papermc/asm/rules/TestRuleScanner.java @@ -0,0 +1,66 @@ +package io.papermc.asm.rules; + +import io.papermc.asm.rules.builder.matcher.method.MethodType; +import io.papermc.asm.rules.method.DirectStaticRewrite; +import java.lang.constant.ClassDesc; +import org.junit.jupiter.api.Test; +import org.objectweb.asm.Opcodes; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class TestRuleScanner { + + private static final String DUMMY_CLASS = "some.pkg.EpicClass"; + + @Test + void testRuleScanner() { + final RewriteRule rule = RuleScanner.scan(MethodHolder.class); + assertNotSame(RewriteRule.EMPTY, rule); + assertInstanceOf(RewriteRule.Chain.class, rule); + final RewriteRule.Chain chain = (RewriteRule.Chain) rule; + assertEquals(2, chain.rules().size()); + + final DirectStaticRewrite convert = (DirectStaticRewrite) chain.rules().get(0); + assertEquals(ClassDesc.of(DUMMY_CLASS), convert.owners().iterator().next()); + final boolean matches1 = convert.methodMatcher().matches(Opcodes.INVOKEVIRTUAL, false, "convert", "(Ljava/lang/String;)Ljava/lang/String;"); + assertTrue(matches1); + + final DirectStaticRewrite notTheName = (DirectStaticRewrite) chain.rules().get(1); + assertEquals(ClassDesc.of(DUMMY_CLASS), notTheName.owners().iterator().next()); + final boolean matches2 = notTheName.methodMatcher().matches(Opcodes.INVOKESTATIC, false, "getById", "(Ljava/lang/Integer;)Ljava/lang/String;"); + assertTrue(matches2); + } + + @Test + void testEmptyRuleScanner() { + final RewriteRule rule = RuleScanner.scan(BadMethodHolder.class); + assertSame(RewriteRule.EMPTY, rule); + } + + public static final class MethodHolder { + + @DirectStaticRewrite.Wrapper(owners = DUMMY_CLASS, type = MethodType.VIRTUAL) + public static String convert(final MethodHolder owner, final String input) { + return input; + } + + @DirectStaticRewrite.Wrapper(owners = DUMMY_CLASS, methodName = "getById", type = MethodType.STATIC) + public static String notTheName(final Integer input) { + return String.valueOf(input); + } + } + + public static final class BadMethodHolder { + static String convert(final String input) { + return input; // not public + } + + public String other(final Integer input) { + return String.valueOf(input); // not static + } + } +} diff --git a/src/test/java/io/papermc/asm/rules/rename/RenameRuleTest.java b/src/test/java/io/papermc/asm/rules/rename/RenameRuleTest.java index 65b5089..d676240 100644 --- a/src/test/java/io/papermc/asm/rules/rename/RenameRuleTest.java +++ b/src/test/java/io/papermc/asm/rules/rename/RenameRuleTest.java @@ -6,11 +6,14 @@ import io.papermc.asm.TestApiVersionImpl; import io.papermc.asm.TransformerTest; import io.papermc.asm.checks.TransformerCheck; -import io.papermc.asm.versioned.MappedVersionRuleFactory; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import io.papermc.asm.versioned.MergingVersionRuleFactory; import io.papermc.asm.versioned.VersionedRuleFactory; import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; import java.util.HashMap; import java.util.Map; +import java.util.Set; import java.util.TreeMap; import java.util.function.Function; import org.jetbrains.annotations.Nullable; @@ -39,6 +42,7 @@ void testRenamerRule(final TransformerCheck check) { }) .annotationAttribute(TestAnnotation.class, "single", "value") .methodByClass(TestAnnotation.class, "single", methodDesc("()Ldata/types/rename/TestEnum;"), "value") + .methodPredicate(TEST_ENUM, MethodMatcher.create(Set.of("method1", "method2"), b -> b.returnType().equals(ConstantDescs.CD_void) && b.parameterList().contains(ConstantDescs.CD_int)), s -> "renamed_" + s) .build(); check.run(rule); @@ -64,7 +68,7 @@ void testVersionedRenamerRule() { .build() ); - final VersionedRuleFactory factory = MappedVersionRuleFactory.mergeable(new TreeMap<>(versions)); + final VersionedRuleFactory factory = MergingVersionRuleFactory.mergeable(new TreeMap<>(versions)); final RenameRule ruleOne = (RenameRule) factory.createRule(ApiVersions.ONE); final RenameRule ruleTwo = (RenameRule) factory.createRule(ApiVersions.TWO); assertEquals("value", annotationMethod("single").apply(ruleOne)); diff --git a/src/test/java/io/papermc/asm/versioned/TestVersionedRuleScanner.java b/src/test/java/io/papermc/asm/versioned/TestVersionedRuleScanner.java new file mode 100644 index 0000000..3af8ebc --- /dev/null +++ b/src/test/java/io/papermc/asm/versioned/TestVersionedRuleScanner.java @@ -0,0 +1,44 @@ +package io.papermc.asm.versioned; + +import io.papermc.asm.ApiVersions; +import io.papermc.asm.TestApiVersionImpl; +import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.method.MethodType; +import io.papermc.asm.rules.method.DirectStaticRewrite; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +class TestVersionedRuleScanner { + + private static final String DUMMY_CLASS = "some.pkg.EpicClass"; + + @Test + void testVersionedRuleScanner() { + final VersionedRuleScanner scanner = new VersionedRuleScanner(s -> new TestApiVersionImpl(Integer.parseInt(s))); + + final VersionedRuleFactory factory = scanner.scan(MethodHolder.class); + + final RewriteRule rule = factory.createRule(ApiVersions.ONE); + assertEquals(2, ((RewriteRule.Chain) rule).rules().size()); + + final RewriteRule rule2 = factory.createRule(ApiVersions.TWO); + assertInstanceOf(DirectStaticRewrite.class, rule2); + } + + public static final class MethodHolder { + + @DirectStaticRewrite.Wrapper(owners = DUMMY_CLASS, type = MethodType.VIRTUAL) + @Version("1") + public static String convert(final MethodHolder owner, final String input) { + return input; + } + + @Version("2") + @DirectStaticRewrite.Wrapper(owners = DUMMY_CLASS, methodName = "getById", type = MethodType.STATIC) + public static String notTheName(final Integer input) { + return String.valueOf(input); + } + } +} diff --git a/src/testData/java/data/rename/RenameTest.java b/src/testData/java/data/rename/RenameTest.java index 9533019..1232421 100644 --- a/src/testData/java/data/rename/RenameTest.java +++ b/src/testData/java/data/rename/RenameTest.java @@ -22,6 +22,9 @@ public static void entry() throws ReflectiveOperationException { System.out.println(fb); final TestEnum ea = TestEnum.valueOf("Ea"); System.out.println(ea); + + a.method1(1); + fb.method2(2); } private static void checkAnnotation(final AnnotatedElement element) { diff --git a/src/testData/java/data/types/rename/TestEnum.java b/src/testData/java/data/types/rename/TestEnum.java index 0a46d4f..9714c3f 100644 --- a/src/testData/java/data/types/rename/TestEnum.java +++ b/src/testData/java/data/types/rename/TestEnum.java @@ -7,4 +7,10 @@ public enum TestEnum { FB, // FB and Ea have the same string hashCode Ea, ; + + public void method1(final int param1) { + } + + public void method2(final int param1) { + } } diff --git a/src/testData/resources/expected/data/rename/RenameTest.class b/src/testData/resources/expected/data/rename/RenameTest.class index d55c38b7b6d973051e3a4601d774b9b5e8e03459..bb91b85231dc34b6c23ccb14d4df9c0c5172f9fa 100644 GIT binary patch delta 1145 zcma))-)|IE6vsbzW_D*fJH;}_6~Uw}P-tfhYg<7mi}J%lT3SFV1w@f8UAiFMVp|GY zv4HYJQGR#@1rdDPHzOK1t%=0M2NR!s3h3wTe1@+ez!o zj$fVP1=hK&57@v)J!O@=9326h*sPb^%>p_Dy4a%o?QE)ttuEUFw)2v{Wp^ig1G2oV zAK3F{U0=X1cIz6aL%pi|ocrp?ciF^Nl{&#&J}z%7>h-;3y`1%q;<1rJDL+&gc>NLI zfOlEtbCSvb)LH{h&(zK+8XBAQkI9DADDS7paaOTJAGOo^w`91wi}NOTK~Zx!Kc3GX z$(IJRy<;PXN`o#RD!gKTw77A2prEiDn?%+}eC%_POL~F3XvGz-8t{q7eviDzF^_SN z<1)xioqZi1H%zo`=MIlA0u~ufC;3v}bZZap!QvIU4~xCLCO$;5Pta(AF^;k8fVii4 znt!jW|L3}{z1VSwG2_M@$B`uol4ARHt(R^)Aln-RJ_BNdI8IRDpr9(|NY$oa;$Otu z;`ivHmua8a@+}Qfg-qBo#YDG&i~(%|dIU5Jh@1+iGKmWB-IX>AbJR+z_0L{raz4E* zV7F*6ht|o)%V=g$C_+266eUt@7UhQ|vN$YxL;a0-9!n(JCq0kmF`LeKMm{MKWX1GP zh__^yXQMDTJ<0PHKaq=hPjgNoe2s=_K2S{2U17%z+!>et5|EbZAp`>1O+rQEHo8I1tpZEYig>H$BTNxP!em$9hJz+$tW-D->a6d);CyZ=gO>! z!cQv*YIOuHi^6L$TAUD68-;B#@{)q)Mq!H}z2DDFUN18*{H)C5;pb)Mgg47ng}2H$ z(G<785gLj+Vtc55$TL_j7Zplu&n$mlaxc#EKa>q-I*pZ2G`wkYjF2&}^?%;ZpSxn( A(f|Me delta 1033 zcma))+fz+(6vscmz0Yay&e=8Drd$RIk$u#O3K1F!aVkm~m*OB5Ig#AsxaEHTJ$dqY z#*1;8QXY(F9=!4w_%E>b&M-_5X7*ldeb@E-Tfg6nX(I)ZkKfMx!Vqcw+?ro@ zg{wZeX0yv?w@u2X-{!a?w6USR-sX;PHMh0c+*1_kgjJMd@xWoe&+Q?Pw6Y7+Ef}=Q zehhZdF4{%0Q&3+Q$4Ig5pr{$l;@`ZYzw_$$UdNd`dPXo}uCys-9{YtNtRkPJSc*;991zRkp!j|2hk|`96lbW-pv>+^~&~qySWM>JQ;<-x&>93Jk`eugd?yU?{ z-P;**-MbmG-TN6r-WZQx3Jt{*k&Wd1kXtq^hUET5Hcs*<#kXOS|A#b~sPv_La)u+m NkE6u=*ZQBg^Alp)w=4hv diff --git a/src/testDataNewTargets/java/data/types/rename/RenamedTestEnum.java b/src/testDataNewTargets/java/data/types/rename/RenamedTestEnum.java index ac42c78..1ae2e8d 100644 --- a/src/testDataNewTargets/java/data/types/rename/RenamedTestEnum.java +++ b/src/testDataNewTargets/java/data/types/rename/RenamedTestEnum.java @@ -7,4 +7,10 @@ public enum RenamedTestEnum { FOUR, FIVE, ; + + public void renamed_method1(final int param1) { + } + + public void renamed_method2(final int param1) { + } } From 61a26512e1fc5b31a980b1677df7dffd4ddc5480 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 28 Feb 2026 11:14:36 -0800 Subject: [PATCH 2/6] Fix checkstyle issues --- src/main/java/io/papermc/asm/rules/method/StaticRewrite.java | 1 + .../java/io/papermc/asm/rules/classes/LegacyEnum.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/io/papermc/asm/rules/method/StaticRewrite.java b/src/main/java/io/papermc/asm/rules/method/StaticRewrite.java index 7366002..bc503ce 100644 --- a/src/main/java/io/papermc/asm/rules/method/StaticRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/StaticRewrite.java @@ -25,6 +25,7 @@ public interface StaticRewrite extends MethodRewriteRule { /** * Transforms the intermediate descriptor to the final * descriptor that will be used in the rewritten bytecode. + * *

* Intermediate means that it has been modified from the * original accounting for the virtual/interface/static/constructor-ness diff --git a/src/mainForNewTargets/java/io/papermc/asm/rules/classes/LegacyEnum.java b/src/mainForNewTargets/java/io/papermc/asm/rules/classes/LegacyEnum.java index bc14d8e..3063548 100644 --- a/src/mainForNewTargets/java/io/papermc/asm/rules/classes/LegacyEnum.java +++ b/src/mainForNewTargets/java/io/papermc/asm/rules/classes/LegacyEnum.java @@ -5,6 +5,7 @@ /** * This type needs to be implemented by the implementation of the * type that was previously an enum and is now an interface. + * *

* Types need to have static methods for {@code values()} * and {@code valueOf(String)}. From 7c266a4c6c0aefbc3ea8bb3488bd5f7630fb0921 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 28 Feb 2026 11:37:07 -0800 Subject: [PATCH 3/6] Update java to 21 and asm --- .github/workflows/build.yml | 2 +- build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- .../io/papermc/asm/rules/TestRuleScanner.java | 24 ++++++++++++------ .../ClassToInterfaceRedirectUser.class | Bin 2611 -> 2611 bytes .../data/classes/ClassToInterfaceUser.class | Bin 2543 -> 2543 bytes .../data/classes/EnumToInterfaceUser.class | Bin 3494 -> 3494 bytes .../fields/FieldToMethodSameOwnerUser.class | Bin 839 -> 839 bytes .../methods/inplace/SubTypeReturnUser.class | Bin 2108 -> 2108 bytes .../methods/inplace/SuperTypeParamUser.class | Bin 2066 -> 2066 bytes .../methods/statics/MoveToInstanceUser.class | Bin 2254 -> 2254 bytes .../data/methods/statics/PlainUser.class | Bin 2607 -> 2607 bytes .../statics/param/ParamDirectUser.class | Bin 3411 -> 3411 bytes .../statics/param/ParamFuzzyUser.class | Bin 3789 -> 3789 bytes .../statics/returns/ReturnDirectUser.class | Bin 2443 -> 2443 bytes .../returns/ReturnDirectWithContextUser.class | Bin 2513 -> 2513 bytes .../expected/data/rename/RenameTest.class | Bin 2990 -> 2990 bytes 17 files changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8797ed0..99511c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 17 ] + java: [ 21 ] fail-fast: true steps: - uses: actions/checkout@v6 diff --git a/build.gradle.kts b/build.gradle.kts index a8f07a8..4b73498 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,7 +22,7 @@ allprojects { indra { javaVersions { - target(17) + target(21) strictVersions(true) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 10ff8bf..07a13eb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -asm = "9.8" +asm = "9.9.1" junit = "5.13.4" junitPlatform = "1.13.4" indra = "3.2.0" diff --git a/src/test/java/io/papermc/asm/rules/TestRuleScanner.java b/src/test/java/io/papermc/asm/rules/TestRuleScanner.java index 6821f93..3d7097d 100644 --- a/src/test/java/io/papermc/asm/rules/TestRuleScanner.java +++ b/src/test/java/io/papermc/asm/rules/TestRuleScanner.java @@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -24,15 +25,22 @@ void testRuleScanner() { final RewriteRule.Chain chain = (RewriteRule.Chain) rule; assertEquals(2, chain.rules().size()); - final DirectStaticRewrite convert = (DirectStaticRewrite) chain.rules().get(0); - assertEquals(ClassDesc.of(DUMMY_CLASS), convert.owners().iterator().next()); - final boolean matches1 = convert.methodMatcher().matches(Opcodes.INVOKEVIRTUAL, false, "convert", "(Ljava/lang/String;)Ljava/lang/String;"); - assertTrue(matches1); + DirectStaticRewrite convert = null; + DirectStaticRewrite notTheName = null; - final DirectStaticRewrite notTheName = (DirectStaticRewrite) chain.rules().get(1); - assertEquals(ClassDesc.of(DUMMY_CLASS), notTheName.owners().iterator().next()); - final boolean matches2 = notTheName.methodMatcher().matches(Opcodes.INVOKESTATIC, false, "getById", "(Ljava/lang/Integer;)Ljava/lang/String;"); - assertTrue(matches2); + for (RewriteRule r : chain.rules()) { + final DirectStaticRewrite rewrite = (DirectStaticRewrite) r; + assertEquals(ClassDesc.of(DUMMY_CLASS), rewrite.owners().iterator().next()); + + if (rewrite.methodMatcher().matches(Opcodes.INVOKEVIRTUAL, false, "convert", "(Ljava/lang/String;)Ljava/lang/String;")) { + convert = rewrite; + } else if (rewrite.methodMatcher().matches(Opcodes.INVOKESTATIC, false, "getById", "(Ljava/lang/Integer;)Ljava/lang/String;")) { + notTheName = rewrite; + } + } + + assertNotNull(convert, "Missing convert method rewrite"); + assertNotNull(notTheName, "Missing getById method rewrite"); } @Test diff --git a/src/testData/resources/expected/data/classes/ClassToInterfaceRedirectUser.class b/src/testData/resources/expected/data/classes/ClassToInterfaceRedirectUser.class index bf750630e45882d2de16d15200a2ad971187bb05..dfbc4192af2ce40284ffe7acd394f9e10a94aeaa 100644 GIT binary patch delta 166 zcmdlivRQ=V)W2Q(7#J8FH*%D-@(D1=@Ut>-2{42*GH7Z{W@J^He2h(yHJXvZ3c`EK z$Ud2qT|khZjUkSUA)X7(lErhH!=m cFe?(sih_#8K*fR>3gGIB;OdIt>Pi_R03A~vm;e9( delta 162 zcmdlivRQ=V)W2Q(7#J9AH*%D-3i7it1amQjFod!*gz+$hGem3_XJcaI6JU_xXJz0L zV2EL4(A1dB$f`DZGpitL5+j2Zg!i6Re6u9`X(s<524;pxhA4(;1`sQjA&wy)%t`>V Y5}{(rP_amc0=T*&xVj>^x>AOC07j%8YXATM diff --git a/src/testData/resources/expected/data/classes/ClassToInterfaceUser.class b/src/testData/resources/expected/data/classes/ClassToInterfaceUser.class index b87df880c12977b6f6f14d127002090f4f57e6f4..9d0db0ecdda31a137b134cc50209fe9d04a8175f 100644 GIT binary patch delta 176 zcmaDa{9c&j)W2Q(7#J8FH*zdyvVmpsC?A(OzZpJys#cw8F|{F)%a4GQ=^&Gk{o$3`q>hU{(r{l?oM0 Vhl<5A6vEXN!_^hT)s-qh8~9A&70Vm7$>uGWHUBSF621P*u2@6^8_W%+ACpA;2KY&&trw z#lXSP!NZWpU@E}S#mHcwF*%V%W->Pyk6>^~QD$DcwE%;m0D~#e5TnVg9NCN=lM6Xc yGj?sZNIJtnuX7helCnjrG24)5y244n01`x|1$O-_nd>DeDVm=IUaM?JxY$Agn E03WdxH~;_u diff --git a/src/testData/resources/expected/data/methods/inplace/SuperTypeParamUser.class b/src/testData/resources/expected/data/methods/inplace/SuperTypeParamUser.class index 54fda5fa0efe582d27978d60616efa3c662ae296..2ca8081364b94d537fdda03407d0032e1786c995 100644 GIT binary patch delta 139 zcmbOvFiC*p)W2Q(7#J8FH*y592n#SM@v}1U3NQ!=FlY%dxG^%AO?F@upZuR$R$McT zpN&Bfh&{O&ycoRM8GLvcd>Q;UXRx=U}o@O@MQ2}0I|G*EFUn-gTW6f Q=D`pJmyLqU#xi&T02{v-9RL6T diff --git a/src/testData/resources/expected/data/methods/statics/MoveToInstanceUser.class b/src/testData/resources/expected/data/methods/statics/MoveToInstanceUser.class index 8134cfa7d62adeb733525f526ce1b83ae25fbd3c..a658db0bd32dfba9724520042aa7ca6e6e551797 100644 GIT binary patch delta 133 zcmX>ncutVx)W2Q(7#J8FH*%a|5f)(3=I3PK6<`n)U{Drd2xeq3oqU#2Q#L0tFI_*l zq$o2l-I||`A(V?Dj3JzzA%ce?k|ApIQx+!1$@*-I8G|=pW}Cvu7(6+H!(KgvftkUN Y!Ji?30mKRfvVy=YKZX>zSPDZB0EDp}8UO$Q delta 125 zcmX>ncutVx)W2Q(7#J9AH*%a|5#(oM@aJL(U}rfro2598G5SX{Ff#-& n1Tq9MfLI|wRw$Ge24#gaL@-2x#R3?L;p&Rv>dN5iDi|UGa_J)z delta 167 zcmZ24vR;Ja)W2Q(7#J9AH*)l`3i7it1adJ1F$A+SgzzwgGK6h*Wn*Gw7hsU%XJz1; z{E<~<@+ubD$@f@A851WjU{#zf$|gVAoqaW9)a1|XYK)1Sr8tf;`UfyDGlVllFhnwd iSkXXM43rfMWyLYXGbDh;!WoL;>Wbm&%HZlM7!m-sq#}6$ diff --git a/src/testData/resources/expected/data/methods/statics/param/ParamDirectUser.class b/src/testData/resources/expected/data/methods/statics/param/ParamDirectUser.class index cba05efc8987c88de42866b6984b7434e46c8162..66310bee4c982645739b21f13144a56eda21181f 100644 GIT binary patch delta 185 zcmcaCbyXaS1Re^RqGV2rxuV)@OHNiDP8&oBWT%dU80sFc&`u zgMA|r$IX3G%ZsL~t=gGDNX6MDsAjFvM;aU}s|F6<|;X%J2v< zBr-BsOn%6zHhBS?5SIXhEn}s-P85uJ+PvF#G zX3UtplgHaXoPn7kjv<~QfdRxyVn}940kh(OEQnYfLmERmRICcFt_H5I2ClB2AsqnF Co+VWP diff --git a/src/testData/resources/expected/data/methods/statics/param/ParamFuzzyUser.class b/src/testData/resources/expected/data/methods/statics/param/ParamFuzzyUser.class index 2cfcf14253db0f9ee64ca2d4cdc4d3ef75312861..9b38a69ff1219efcae32b723f33c338dbc6cda04 100644 GIT binary patch delta 167 zcmX>rdsdd?)W2Q(7#J8FH*(Bk;}T#{<7Z>w5n#xeyr12LHHVSGPh)Zbi`Zm24qKLd zMh3UZfsCq?TR0R2fyxTG7>XE**%?ZB7)lw+Hg9HQV%)rx<0h+r0RuBbDnlAWIs=H6 o$&kg64Q8bRSrD;QhFpd`s8|<64^&+r!vv^UA5<)rVKT!M0Pg=JivR!s delta 175 zcmX>rdsdd?)W2Q(7#J9AH*(Bk6Xa)ONaJEiXUJe@$mC(jV#waSn4O7{SAancD8nPb zkk80qF*%S?b+SIYFqZ&>0Y58)m;giZ5cc1_lPljU28l@&XLH{A>*D0t`I-tPDy(W*`?s5JNCKLkJH;C_~uh jI2IGN+d>Q;0AS{14D+SI5cc1_lP(jU28lg8Xa@zFZ7`4F2p40Xz(W3_+W7SeO|31Q>LIGVB5j kJp8N-N}KaozcH)(FfcO&GlVdNGC){ia8?SOmBtVX0PQ>xSpWb4 diff --git a/src/testData/resources/expected/data/methods/statics/returns/ReturnDirectWithContextUser.class b/src/testData/resources/expected/data/methods/statics/returns/ReturnDirectWithContextUser.class index da90a91fcde6058766544ae2a0b5fbc4b7740de8..2cbbe8349f96fe1cf0a8ce87f04179bb807ac720 100644 GIT binary patch delta 99 zcmca8d{LO=)W2Q(7#J8FH*%!0$O|y&^0P6p3o!8Tvoa_FnSopkK@7p{3?V!Wp$uW0 j>sgo>H}|tjv#19!Ff;ft_%ir0Kv@28RtlVz#^474@9Ysq delta 100 zcmca8d{LO=)W2Q(7#J9AH*%!02=cQr_;NA$G5E7H1n@8fG6ZezWnp6E6JXE<%CHMC k@bI%TC~fX%m1a@*VPIwmW(Z*jWq`24;H(rlD~%x(02qrAZU6uP diff --git a/src/testData/resources/expected/data/rename/RenameTest.class b/src/testData/resources/expected/data/rename/RenameTest.class index bb91b85231dc34b6c23ccb14d4df9c0c5172f9fa..867b23d883641cde95ba8d249664941306207616 100644 GIT binary patch delta 17 ZcmZ1{zD}Iu)W2Q(7#J8FH*zfF1^_^}27Ukl delta 17 ZcmZ1{zD}Iu)W2Q(7#J9AH*zfF1^_^#26_Mh From e67104ac4e2af896e58736b9f28fb891888c9a78 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 28 Feb 2026 11:43:43 -0800 Subject: [PATCH 4/6] Fix deprecation warnings --- .../rules/rename/PredicateMethodRemapper.java | 3 ++- .../io/papermc/asm/rules/rename/RenameRule.java | 17 +++++++++++------ .../asm/rules/rename/RenameRuleBuilderImpl.java | 10 ++++++---- .../io/papermc/asm/rules/TestRuleScanner.java | 3 +-- .../asm/rules/rename/RenameRuleTest.java | 7 ++++--- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java b/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java index b99330f..fee4d9e 100644 --- a/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java +++ b/src/main/java/io/papermc/asm/rules/rename/PredicateMethodRemapper.java @@ -10,7 +10,8 @@ public class PredicateMethodRemapper extends Remapper { private final Map>> methodRemaps; - public PredicateMethodRemapper(final Map>> methodRemaps) { + public PredicateMethodRemapper(final int api, final Map>> methodRemaps) { + super(api); this.methodRemaps = methodRemaps; } diff --git a/src/main/java/io/papermc/asm/rules/rename/RenameRule.java b/src/main/java/io/papermc/asm/rules/rename/RenameRule.java index 3175d00..f448bf0 100644 --- a/src/main/java/io/papermc/asm/rules/rename/RenameRule.java +++ b/src/main/java/io/papermc/asm/rules/rename/RenameRule.java @@ -14,16 +14,18 @@ public final class RenameRule implements RewriteRule.Delegate, Mergeable { - public static RenameRuleBuilder builder() { - return new RenameRuleBuilderImpl(); + public static RenameRuleBuilder builder(final int api) { + return new RenameRuleBuilderImpl(api); } + private final int api; private final Map renames; private final Map enumFieldRenames; private final Map>> predicateMethodRemaps; private @Nullable RewriteRule rule; - public RenameRule(final Map renames, final Map enumFieldRenames, final Map>> predicateMethodRemaps) { + public RenameRule(final int api, final Map renames, final Map enumFieldRenames, final Map>> predicateMethodRemaps) { + this.api = api; this.renames = Map.copyOf(renames); this.enumFieldRenames = Map.copyOf(enumFieldRenames); this.predicateMethodRemaps = Map.copyOf(predicateMethodRemaps); @@ -40,8 +42,8 @@ public Map enumFieldRenames() { @Override public RewriteRule delegate() { if (this.rule == null) { - final Remapper remapper = new SimpleRemapper(Map.copyOf(this.renames)); - final Remapper predicateRemapper = new PredicateMethodRemapper(this.predicateMethodRemaps); + final Remapper remapper = new SimpleRemapper(this.api, Map.copyOf(this.renames)); + final Remapper predicateRemapper = new PredicateMethodRemapper(this.api, this.predicateMethodRemaps); final List rules = new ArrayList<>(this.enumFieldRenames.size() + 1); this.enumFieldRenames.forEach((classDesc, enumRenamer) -> { @@ -57,6 +59,9 @@ public RewriteRule delegate() { @Override public RenameRule merge(final RenameRule other) { + if (this.api != other.api) { + throw new IllegalArgumentException("Cannot merge rules with different API versions"); + } final Map regularRenames = new HashMap<>(this.renames); regularRenames.putAll(other.renames); @@ -76,6 +81,6 @@ public RenameRule merge(final RenameRule other) { newPredicateRenames.put(s, Map.copyOf(newMethods)); }); - return new RenameRule(regularRenames, enumFieldRenames, newPredicateRenames); + return new RenameRule(this.api, regularRenames, enumFieldRenames, newPredicateRenames); } } diff --git a/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java b/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java index ed97b79..e14171e 100644 --- a/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java +++ b/src/main/java/io/papermc/asm/rules/rename/RenameRuleBuilderImpl.java @@ -14,13 +14,15 @@ final class RenameRuleBuilderImpl implements RenameRuleBuilder { - RenameRuleBuilderImpl() { - } - + private final int api; final Map mappings = new HashMap<>(); final Map enumValueOfFieldRenames = new HashMap<>(); final Map>> predicateMethodRenames = new HashMap<>(); + RenameRuleBuilderImpl(final int api) { + this.api = api; + } + @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); @@ -79,6 +81,6 @@ public RenameRuleBuilder editEnum(final ClassDesc enumTypeDesc, final Consumer { builder @@ -51,14 +52,14 @@ void testRenamerRule(final TransformerCheck check) { @Test void testVersionedRenamerRule() { final Map versions = new HashMap<>(); - versions.put(ApiVersions.ONE, RenameRule.builder() + versions.put(ApiVersions.ONE, RenameRule.builder(Opcodes.ASM9) .methodByClass(TestAnnotation.class, "single", methodDesc("()Ldata/types/rename/TestEnum;"), "value") .editEnum(TEST_ENUM, builder -> builder .rename("A", "ONE") ) .build() ); - versions.put(ApiVersions.THREE, RenameRule.builder() + versions.put(ApiVersions.THREE, RenameRule.builder(Opcodes.ASM9) .methodByClass(TestAnnotation.class, "newValue", methodDesc("()Ldata/types/rename/TestEnum;"), "value") .annotationAttribute(TestAnnotation.class, "newValue", "value") .editEnum(TEST_ENUM, builder -> builder From 688591cd6e5b3f380a4cdee1e60f5f0df1521fe3 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 28 Feb 2026 12:06:22 -0800 Subject: [PATCH 5/6] update secrets --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99511c2..2569ea2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,13 +37,13 @@ jobs: if: "${{ env.STATUS != 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}" run: ./gradlew publish env: - ORG_GRADLE_PROJECT_paperSnapshotsUsername: "${{ secrets.PAPER_REPO_USER }}" - ORG_GRADLE_PROJECT_paperSnapshotsPassword: "${{ secrets.PAPER_REPO_PASSWORD }}" + ORG_GRADLE_PROJECT_paperSnapshotsUsername: "${{ secrets.ARTIFACTORY_USERNAME }}" + ORG_GRADLE_PROJECT_paperSnapshotsPassword: "${{ secrets.ARTIFACTORY_PASSWORD }}" - name: Publish Release if: "${{ env.STATUS == 'release' && github.event_name == 'release' }}" run: ./gradlew publish env: - ORG_GRADLE_PROJECT_paperReleasesUsername: "${{ secrets.PAPER_REPO_USER }}" - ORG_GRADLE_PROJECT_paperReleasesPassword: "${{ secrets.PAPER_REPO_PASSWORD }}" + ORG_GRADLE_PROJECT_paperReleasesUsername: "${{ secrets.ARTIFACTORY_USERNAME }}" + ORG_GRADLE_PROJECT_paperReleasesPassword: "${{ secrets.ARTIFACTORY_PASSWORD }}" ORG_GRADLE_PROJECT_signingKey: "${{ secrets.SIGNING_KEY }}" ORG_GRADLE_PROJECT_signingPassword: "${{ secrets.SIGNING_PASSWORD }}" From 46759e2baf61a331b7dfa6492fa007774ea21528 Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Sat, 28 Feb 2026 12:07:51 -0800 Subject: [PATCH 6/6] Update repo URLs --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4b73498..b17d6c2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,8 +26,8 @@ allprojects { strictVersions(true) } - publishSnapshotsTo("paperSnapshots", "https://repo.papermc.io/repository/maven-snapshots/") - publishReleasesTo("paperReleases", "https://repo.papermc.io/repository/maven-releases/") + publishSnapshotsTo("paperSnapshots", "https://artifactory.papermc.io/artifactory/snapshots/") + publishReleasesTo("paperReleases", "https://artifactory.papermc.io/artifactory/releases/") signWithKeyFromProperties("signingKey", "signingPassword") apache2License()