From fa6503d5633ae7ad3238a2bc615e5302e2a5f43a Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Mon, 30 Dec 2024 23:00:27 -0800 Subject: [PATCH] finished versioning all the various rules --- .../io/papermc/asm/rules/RewriteRule.java | 4 + .../asm/rules/builder/RuleFactory.java | 18 +-- .../asm/rules/builder/RuleFactoryImpl.java | 8 +- .../rules/classes/ClassToInterfaceRule.java | 11 ++ .../asm/rules/field/FieldToMethodRewrite.java | 18 +++ .../asm/rules/method/DirectStaticRewrite.java | 12 ++ .../asm/rules/method/MoveInstanceMethod.java | 12 ++ .../method/params/DirectParameterRewrite.java | 5 +- .../method/params/FuzzyParameterRewrite.java | 5 +- .../method/params/SuperTypeParamRewrite.java | 19 +-- .../method/returns/DirectReturnRewrite.java | 13 ++ .../method/returns/SubTypeReturnRewrite.java | 19 +-- .../papermc/asm/rules/rename/RenameRule.java | 37 ++---- .../CachingVersionedRuleFactory.java | 3 + .../versioned/MappedVersionRuleFactory.java | 27 +++++ .../io/papermc/asm/versioned/Mergeable.java | 8 ++ .../OwnedVersionedRuleFactoryFactory.java | 112 ++++++++++++++---- .../OwnedVersionedRuleFactoryFactoryImpl.java | 57 ++++++++- .../asm/versioned/VersionedRuleFactory.java | 4 + .../TargetedMethodMatcherWithHandler.java | 7 ++ .../versioned/matcher/VersionedMatcher.java | 53 +++++++++ .../matcher/VersionedMatcherBase.java | 29 ----- .../matcher/VersionedMatcherBuilder.java | 11 ++ .../matcher/VersionedMatcherBuilderImpl.java | 24 ++++ .../matcher/VersionedMethodMatcher.java | 26 ---- .../VersionedMethodMatcherBuilder.java | 13 -- .../VersionedMethodMatcherBuilderImpl.java | 26 ---- ...rgetedMethodMatcherWithHandlerBuilder.java | 16 +++ ...edMethodMatcherWithHandlerBuilderImpl.java | 20 ++++ .../VersionedTargetedMethodMatcher.java | 27 ----- ...VersionedTargetedMethodMatcherBuilder.java | 13 -- ...ionedTargetedMethodMatcherBuilderImpl.java | 26 ---- src/test/java/io/papermc/asm/ApiVersion.java | 29 +---- .../asm/rules/methods/MethodRewritesTest.java | 42 ++++--- .../asm/rules/rename/RenameRuleTest.java | 24 +++- 35 files changed, 476 insertions(+), 302 deletions(-) create mode 100644 src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java create mode 100644 src/main/java/io/papermc/asm/versioned/Mergeable.java create mode 100644 src/main/java/io/papermc/asm/versioned/matcher/TargetedMethodMatcherWithHandler.java create mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcher.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBase.java create mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilder.java create mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilderImpl.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcher.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilder.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilderImpl.java create mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilder.java create mode 100644 src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilderImpl.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcher.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilder.java delete mode 100644 src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilderImpl.java diff --git a/src/main/java/io/papermc/asm/rules/RewriteRule.java b/src/main/java/io/papermc/asm/rules/RewriteRule.java index c1fe310..0510c4a 100644 --- a/src/main/java/io/papermc/asm/rules/RewriteRule.java +++ b/src/main/java/io/papermc/asm/rules/RewriteRule.java @@ -49,6 +49,10 @@ static RewriteRule chain(final RewriteRule... rules) { return chain(Arrays.asList(rules)); } + static RewriteRule chain(final RewriteRule rule1, final RewriteRule rule2) { + return chain(List.of(rule1, rule2)); + } + static RewriteRule chain(final Collection rules) { final List filteredRules = rules.stream().filter(r -> r != EMPTY).toList(); if (filteredRules.isEmpty()) { diff --git a/src/main/java/io/papermc/asm/rules/builder/RuleFactory.java b/src/main/java/io/papermc/asm/rules/builder/RuleFactory.java index ea909a2..f86c0a4 100644 --- a/src/main/java/io/papermc/asm/rules/builder/RuleFactory.java +++ b/src/main/java/io/papermc/asm/rules/builder/RuleFactory.java @@ -30,14 +30,11 @@ static RuleFactory.Factory combine(final RuleFactory.Factory... factories) { void plainStaticRewrite(ClassDesc newOwner, MethodMatcher methodMatcher, String staticMethodName); - default void changeParamToSuper(final Class oldParamType, final Class newParamType, final MethodMatcher methodMatcher) { - if (!newParamType.isAssignableFrom(oldParamType)) { - throw new IllegalArgumentException(newParamType + " is not a superclass of " + oldParamType); - } - this.changeParamToSuper(desc(oldParamType), desc(newParamType), methodMatcher); + default void changeParamToSuper(final Class newParamType, final TargetedMethodMatcher methodMatcher) { + this.changeParamToSuper( desc(newParamType), methodMatcher); } - void changeParamToSuper(ClassDesc legacyParamType, ClassDesc newParamType, MethodMatcher methodMatcher); + void changeParamToSuper(ClassDesc newParamType, TargetedMethodMatcher methodMatcher); default void changeParamFuzzy(final Class newParamType, final Method staticHandler, final TargetedMethodMatcher targetedMethodMatcher) { this.changeParamFuzzy(desc(newParamType), staticHandler, targetedMethodMatcher); @@ -51,14 +48,11 @@ default void changeParamDirect(final Class newParamType, final Method staticH void changeParamDirect(ClassDesc newParamType, Method staticHandler, TargetedMethodMatcher targetedMethodMatcher); - default void changeReturnTypeToSub(final Class oldReturnType, final Class newReturnType, final MethodMatcher methodMatcher) { - if (!oldReturnType.isAssignableFrom(newReturnType)) { - throw new IllegalArgumentException(newReturnType + " is not a subclass of " + oldReturnType); - } - this.changeReturnTypeToSub(desc(oldReturnType), desc(newReturnType), methodMatcher); + default void changeReturnTypeToSub(final Class newReturnType, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeToSub(desc(newReturnType), methodMatcher); } - void changeReturnTypeToSub(ClassDesc oldReturnType, ClassDesc newReturnType, MethodMatcher methodMatcher); + void changeReturnTypeToSub(ClassDesc newReturnType, TargetedMethodMatcher methodMatcher); default void changeReturnTypeDirect(final Class newReturnType, final Method staticHandler, final TargetedMethodMatcher targetedMethodMatcher) { this.changeReturnTypeDirect(desc(newReturnType), staticHandler, targetedMethodMatcher); diff --git a/src/main/java/io/papermc/asm/rules/builder/RuleFactoryImpl.java b/src/main/java/io/papermc/asm/rules/builder/RuleFactoryImpl.java index 4e7ed90..3dabe7e 100644 --- a/src/main/java/io/papermc/asm/rules/builder/RuleFactoryImpl.java +++ b/src/main/java/io/papermc/asm/rules/builder/RuleFactoryImpl.java @@ -50,8 +50,8 @@ public void plainStaticRewrite(final ClassDesc newOwner, final MethodMatcher met } @Override - public void changeParamToSuper(final ClassDesc legacyParamType, final ClassDesc newParamType, final MethodMatcher methodMatcher) { - this.addRule(new SuperTypeParamRewrite(this.owners, methodMatcher, legacyParamType, newParamType)); + public void changeParamToSuper(final ClassDesc newParamType, final TargetedMethodMatcher methodMatcher) { + this.addRule(new SuperTypeParamRewrite(this.owners, methodMatcher, newParamType)); } @Override @@ -65,8 +65,8 @@ public void changeParamDirect(final ClassDesc newParamType, final Method staticH } @Override - public void changeReturnTypeToSub(final ClassDesc oldReturnType, final ClassDesc newReturnType, final MethodMatcher methodMatcher) { - this.addRule(new SubTypeReturnRewrite(this.owners, methodMatcher, oldReturnType, newReturnType)); + public void changeReturnTypeToSub(final ClassDesc newReturnType, final TargetedMethodMatcher methodMatcher) { + this.addRule(new SubTypeReturnRewrite(this.owners, methodMatcher, newReturnType)); } @Override 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 b2c4928..644b964 100644 --- a/src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java +++ b/src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java @@ -6,8 +6,11 @@ import io.papermc.asm.rules.method.StaticRewrite; import io.papermc.asm.rules.method.rewrite.MethodRewrite; import io.papermc.asm.rules.method.rewrite.SimpleRewrite; +import io.papermc.asm.versioned.ApiVersion; +import io.papermc.asm.versioned.VersionedRuleFactory; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; +import java.util.NavigableMap; import java.util.Set; import org.checkerframework.checker.nullness.qual.Nullable; import org.objectweb.asm.ClassVisitor; @@ -94,4 +97,12 @@ public void visit(final int version, final int access, final String name, final }; } } + + public record Versioned(NavigableMap versions) implements VersionedRuleFactory { + + @Override + public RewriteRule createRule(final ApiVersion apiVersion) { + return null; + } + } } diff --git a/src/main/java/io/papermc/asm/rules/field/FieldToMethodRewrite.java b/src/main/java/io/papermc/asm/rules/field/FieldToMethodRewrite.java index 008614c..f973d27 100644 --- a/src/main/java/io/papermc/asm/rules/field/FieldToMethodRewrite.java +++ b/src/main/java/io/papermc/asm/rules/field/FieldToMethodRewrite.java @@ -1,7 +1,11 @@ package io.papermc.asm.rules.field; import io.papermc.asm.ClassProcessingContext; +import io.papermc.asm.rules.RewriteRule; import io.papermc.asm.rules.builder.matcher.field.FieldMatcher; +import io.papermc.asm.versioned.ApiVersion; +import io.papermc.asm.versioned.VersionedRuleFactory; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; @@ -91,4 +95,18 @@ MethodTypeDesc desc(final ClassDesc fieldTypeDesc) { delegate.visitMethodInsn(type.opcode(opcode), owner, methodName, type.desc(fieldTypeDesc).descriptorString(), this.isInterfaceMethod); }; } + + public record Versioned(Set owners, @Nullable String getterName, @Nullable String setterName, boolean isInterfaceMethod, VersionedMatcher versions) implements VersionedRuleFactory { + + public Versioned { + if (getterName == null && setterName == null) { + throw new IllegalArgumentException("At least one of getterName or setterName must be non-null"); + } + } + + @Override + public RewriteRule createRule(final ApiVersion apiVersion) { + return this.versions.ruleForVersion(apiVersion, matcher -> new FieldToMethodRewrite(this.owners(), matcher, this.getterName(), this.setterName(), this.isInterfaceMethod())); + } + } } 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 c6095ce..3d19c15 100644 --- a/src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java +++ b/src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java @@ -1,11 +1,15 @@ package io.papermc.asm.rules.method; 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.generate.GeneratedMethodHolder; 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.versioned.ApiVersion; +import io.papermc.asm.versioned.VersionedRuleFactory; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.Set; @@ -49,4 +53,12 @@ public MethodRewrite createConstructo public ClassDesc staticRedirectOwner(final ClassProcessingContext context) { return this.staticRedirectOwner; } + + public record Versioned(Set owners, ClassDesc staticRedirectOwner, @Nullable String staticMethodName, VersionedMatcher versions) implements VersionedRuleFactory { + + @Override + public RewriteRule createRule(final ApiVersion apiVersion) { + return this.versions.ruleForVersion(apiVersion, match -> new DirectStaticRewrite(this.owners(), this.staticMethodName(), match, this.staticRedirectOwner())); + } + } } diff --git a/src/main/java/io/papermc/asm/rules/method/MoveInstanceMethod.java b/src/main/java/io/papermc/asm/rules/method/MoveInstanceMethod.java index f58c55a..0fbae46 100644 --- a/src/main/java/io/papermc/asm/rules/method/MoveInstanceMethod.java +++ b/src/main/java/io/papermc/asm/rules/method/MoveInstanceMethod.java @@ -1,9 +1,13 @@ package io.papermc.asm.rules.method; 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.method.generated.GeneratedStaticRewrite; import io.papermc.asm.rules.method.rewrite.MethodRewrite; +import io.papermc.asm.versioned.ApiVersion; +import io.papermc.asm.versioned.VersionedRuleFactory; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.Set; @@ -56,4 +60,12 @@ public void generateMethod(final GeneratorAdapterFactory factory, final MethodCa public void generateConstructor(final GeneratorAdapterFactory factory, final MethodCallData modified, final ConstructorCallData original) { throw new UnsupportedOperationException("Doesn't work with constructors"); } + + public record Versioned(Set owners, ClassDesc newOwner, String newMethodName, VersionedMatcher versions) implements VersionedRuleFactory { + + @Override + public RewriteRule createRule(final ApiVersion apiVersion) { + return this.versions.ruleForVersion(apiVersion, match -> new MoveInstanceMethod(this.owners(), match, this.newOwner(), this.newMethodName())); + } + } } 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 7f32072..21d9562 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 @@ -6,7 +6,8 @@ import io.papermc.asm.rules.method.generated.TargetedTypeGeneratedStaticRewrite; import io.papermc.asm.versioned.ApiVersion; import io.papermc.asm.versioned.VersionedRuleFactory; -import io.papermc.asm.versioned.matcher.targeted.VersionedTargetedMethodMatcher; +import io.papermc.asm.versioned.matcher.TargetedMethodMatcherWithHandler; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.reflect.Method; import java.util.Set; @@ -22,7 +23,7 @@ */ public record DirectParameterRewrite(Set owners, ClassDesc existingType, TargetedMethodMatcher methodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter, OwnableMethodRewriteRule.Filtered { - public record Versioned(Set owners, ClassDesc existingType, VersionedTargetedMethodMatcher versions) implements VersionedRuleFactory { + public record Versioned(Set owners, ClassDesc existingType, VersionedMatcher versions) implements VersionedRuleFactory { @Override public RewriteRule createRule(final ApiVersion apiVersion) { 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 d48a97b..7e7abd6 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 @@ -8,7 +8,8 @@ import io.papermc.asm.rules.method.rewrite.MethodRewrite; import io.papermc.asm.versioned.ApiVersion; import io.papermc.asm.versioned.VersionedRuleFactory; -import io.papermc.asm.versioned.matcher.targeted.VersionedTargetedMethodMatcher; +import io.papermc.asm.versioned.matcher.TargetedMethodMatcherWithHandler; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; @@ -59,7 +60,7 @@ public MethodRewrite createRewrite(final ClassProcessingContext }); } - public record Versioned(Set owners, ClassDesc existingType, VersionedTargetedMethodMatcher versions) implements VersionedRuleFactory { + public record Versioned(Set owners, ClassDesc existingType, VersionedMatcher versions) implements VersionedRuleFactory { @Override public RewriteRule createRule(final ApiVersion apiVersion) { 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 d78154d..8bed8b4 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,13 +2,13 @@ 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; import io.papermc.asm.rules.method.rewrite.SimpleRewrite; import io.papermc.asm.versioned.ApiVersion; import io.papermc.asm.versioned.VersionedRuleFactory; -import io.papermc.asm.versioned.matcher.VersionedMethodMatcher; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.Set; @@ -21,11 +21,14 @@ * offending parameter in the descriptor and move on. * * @param owners owners of the methods to change - * @param methodMatcher method matcher to find methods with - * @param oldParamType the parameter type that will be found in bytecode that needs to be transformed + * @param methodMatcher 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, MethodMatcher methodMatcher, ClassDesc oldParamType, ClassDesc newParamType) implements OwnableMethodRewriteRule.Filtered { +public record SuperTypeParamRewrite(Set owners, TargetedMethodMatcher methodMatcher, ClassDesc newParamType) implements OwnableMethodRewriteRule.Filtered { + + public ClassDesc oldParamType() { + return this.methodMatcher.targetType(); + } @Override public MethodRewrite rewrite(final ClassProcessingContext context, final boolean isInvokeDynamic, final int opcode, final ClassDesc owner, final String name, final MethodTypeDesc descriptor, final boolean isInterface) { @@ -33,14 +36,14 @@ public MethodRewrite rewrite(final ClassProcessingContext context, final bool } private MethodTypeDesc modifyMethodDescriptor(final MethodTypeDesc methodDescriptor) { - return replaceParameters(methodDescriptor, isEqual(this.oldParamType()), this.newParamType()); + return replaceParameters(methodDescriptor, isEqual(this.methodMatcher().targetType()), this.newParamType()); } - public record Versioned(Set owners, ClassDesc newParamType, VersionedMethodMatcher versions) implements VersionedRuleFactory { + public record Versioned(Set owners, ClassDesc newParamType, VersionedMatcher versions) implements VersionedRuleFactory { @Override public RewriteRule createRule(final ApiVersion apiVersion) { - return this.versions.ruleForVersion(apiVersion, pair -> new SuperTypeParamRewrite(this.owners(), pair.matcher(), pair.legacyType(), this.newParamType())); + return this.versions.ruleForVersion(apiVersion, matcher -> new SuperTypeParamRewrite(this.owners(), matcher, this.newParamType())); } } } 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 7790820..d8be19f 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,8 +1,13 @@ package io.papermc.asm.rules.method.returns; +import io.papermc.asm.rules.RewriteRule; 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; +import io.papermc.asm.versioned.ApiVersion; +import io.papermc.asm.versioned.VersionedRuleFactory; +import io.papermc.asm.versioned.matcher.TargetedMethodMatcherWithHandler; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.reflect.Method; import java.util.Set; @@ -30,4 +35,12 @@ public record DirectReturnRewrite(Set owners, ClassDesc existingType, throw new IllegalArgumentException("staticHandler param type isn't " + existingType); } } + + public record Versioned(Set owners, ClassDesc existingType, VersionedMatcher versions, boolean includeOwnerContext) implements VersionedRuleFactory { + + @Override + public RewriteRule createRule(final ApiVersion apiVersion) { + return this.versions.ruleForVersion(apiVersion, pair -> new DirectReturnRewrite(this.owners(), this.existingType(), pair.matcher(), pair.staticHandler(), this.includeOwnerContext())); + } + } } 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 1cffd49..1c2db57 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,13 +2,13 @@ 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; import io.papermc.asm.rules.method.rewrite.SimpleRewrite; import io.papermc.asm.versioned.ApiVersion; import io.papermc.asm.versioned.VersionedRuleFactory; -import io.papermc.asm.versioned.matcher.VersionedMethodMatcher; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.MethodTypeDesc; import java.util.Set; @@ -19,15 +19,18 @@ * 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 - * @param oldReturnType the return type that will be found in bytecode that needs to be transformed + * @param methodMatcher 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, MethodMatcher methodMatcher, ClassDesc oldReturnType, ClassDesc newReturnType) implements OwnableMethodRewriteRule.Filtered { +public record SubTypeReturnRewrite(Set owners, TargetedMethodMatcher methodMatcher, ClassDesc newReturnType) implements OwnableMethodRewriteRule.Filtered { + + public ClassDesc oldReturnType() { + return this.methodMatcher.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.newReturnType())) { + if (descriptor.returnType().equals(this.methodMatcher().targetType())) { return new SimpleRewrite(opcode, owner, name, this.modifyMethodDescriptor(descriptor), isInterface, isInvokeDynamic); } return null; @@ -37,11 +40,11 @@ private MethodTypeDesc modifyMethodDescriptor(final MethodTypeDesc methodDescrip return methodDescriptor.changeReturnType(this.newReturnType()); } - public record Versioned(Set owners, ClassDesc newReturnType, VersionedMethodMatcher versions) implements VersionedRuleFactory { + public record Versioned(Set owners, ClassDesc newReturnType, VersionedMatcher versions) implements VersionedRuleFactory { @Override public RewriteRule createRule(final ApiVersion apiVersion) { - return this.versions.ruleForVersion(apiVersion, pair -> new SubTypeReturnRewrite(this.owners(), pair.matcher(), pair.legacyType(), this.newReturnType())); + return this.versions.ruleForVersion(apiVersion, matcher -> new SubTypeReturnRewrite(this.owners(), matcher, this.newReturnType())); } } } 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 cb3a104..c86eb48 100644 --- a/src/main/java/io/papermc/asm/rules/rename/RenameRule.java +++ b/src/main/java/io/papermc/asm/rules/rename/RenameRule.java @@ -2,20 +2,17 @@ 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 io.papermc.asm.versioned.Mergeable; 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 { +public final class RenameRule implements RewriteRule.Delegate, Mergeable { public static RenameRuleBuilder builder() { return new RenameRuleBuilderImpl(); @@ -54,26 +51,14 @@ public RewriteRule delegate() { return this.rule; } - public record Versioned(NavigableMap versions) implements VersionedRuleFactory { - - @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); - final Map regularRenames = new HashMap<>(); - final Map 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); - } + @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); + other.enumFieldRenames.forEach((cd, renamer) -> { + enumFieldRenames.merge(cd, renamer, EnumRenamer::overwrite); + }); + return new RenameRule(regularRenames, enumFieldRenames); } } diff --git a/src/main/java/io/papermc/asm/versioned/CachingVersionedRuleFactory.java b/src/main/java/io/papermc/asm/versioned/CachingVersionedRuleFactory.java index ed9bc09..e0411d9 100644 --- a/src/main/java/io/papermc/asm/versioned/CachingVersionedRuleFactory.java +++ b/src/main/java/io/papermc/asm/versioned/CachingVersionedRuleFactory.java @@ -6,6 +6,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.jetbrains.annotations.ApiStatus; +/** + * Caches creating {@link RewriteRule}s for each {@link ApiVersion}. + */ public abstract class CachingVersionedRuleFactory implements VersionedRuleFactory { private final Map cache = new ConcurrentHashMap<>(); diff --git a/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java b/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java new file mode 100644 index 0000000..5efa8a5 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/MappedVersionRuleFactory.java @@ -0,0 +1,27 @@ +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.function.BinaryOperator; + +public record MappedVersionRuleFactory(NavigableMap versions, BinaryOperator mergeFunction) implements VersionedRuleFactory { + + public static > MappedVersionRuleFactory mergeable(final NavigableMap versions) { + return new MappedVersionRuleFactory<>(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/Mergeable.java b/src/main/java/io/papermc/asm/versioned/Mergeable.java new file mode 100644 index 0000000..c237d28 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/Mergeable.java @@ -0,0 +1,8 @@ +package io.papermc.asm.versioned; + +import io.papermc.asm.rules.RewriteRule; + +public interface Mergeable { + + R merge(R other); +} diff --git a/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactory.java b/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactory.java index 570aeb0..6270cc3 100644 --- a/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactory.java +++ b/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactory.java @@ -1,12 +1,16 @@ package io.papermc.asm.versioned; +import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.field.FieldMatcher; import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; -import io.papermc.asm.versioned.matcher.VersionedMethodMatcher; -import io.papermc.asm.versioned.matcher.targeted.VersionedTargetedMethodMatcher; +import io.papermc.asm.versioned.matcher.TargetedMethodMatcherWithHandler; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.reflect.Method; +import java.util.NavigableMap; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; import static io.papermc.asm.util.DescriptorUtils.desc; @@ -16,22 +20,28 @@ static OwnedVersionedRuleFactoryFactory create(final Set owners) { return new OwnedVersionedRuleFactoryFactoryImpl(owners); } - // plain static rewrite + // + default void plainStaticRewrite(final ClassDesc newOwner, final @Nullable String staticMethodName, final ApiVersion apiVersion, final MethodMatcher matcher) { + this.plainStaticRewrite(newOwner, staticMethodName, VersionedMatcher.single(apiVersion, matcher)); + } + + void plainStaticRewrite(ClassDesc newOwner, @Nullable String staticMethodName, VersionedMatcher versions); + // // - default void changeParamToSuper(final Class newParamType, final ApiVersion apiVersion, final Class legacyParamType, final MethodMatcher methodMatcher) { - this.changeParamToSuper(desc(newParamType), apiVersion, desc(legacyParamType), methodMatcher); + default void changeParamToSuper(final Class newParamType, final ApiVersion apiVersion, final TargetedMethodMatcher methodMatcher) { + this.changeParamToSuper(desc(newParamType), apiVersion, methodMatcher); } - default void changeParamToSuper(final ClassDesc newParamType, final ApiVersion apiVersion, final ClassDesc legacyParamType, final MethodMatcher methodMatcher) { - this.changeParamToSuper(newParamType, VersionedMethodMatcher.single(apiVersion, methodMatcher, legacyParamType)); + default void changeParamToSuper(final ClassDesc newParamType, final ApiVersion apiVersion, final TargetedMethodMatcher methodMatcher) { + this.changeParamToSuper(newParamType, VersionedMatcher.single(apiVersion, methodMatcher)); } - default void changeParamToSuper(final Class newParamType, final VersionedMethodMatcher versions) { + default void changeParamToSuper(final Class newParamType, final VersionedMatcher versions) { this.changeParamToSuper(desc(newParamType), versions); } - void changeParamToSuper(ClassDesc newParamType, VersionedMethodMatcher versions); + void changeParamToSuper(ClassDesc newParamType, VersionedMatcher versions); // // @@ -40,14 +50,14 @@ default void changeParamFuzzy(final Class newParamType, final ApiVersion apiV } default void changeParamFuzzy(final ClassDesc newParamType, final ApiVersion apiVersion, final Method staticHandler, final TargetedMethodMatcher targetedMethodMatcher) { - this.changeParamFuzzy(newParamType, VersionedTargetedMethodMatcher.single(apiVersion, targetedMethodMatcher, staticHandler)); + this.changeParamFuzzy(newParamType, VersionedMatcher.single(apiVersion, new TargetedMethodMatcherWithHandler(targetedMethodMatcher, staticHandler))); } - default void changeParamFuzzy(final Class newParamType, final VersionedTargetedMethodMatcher versions) { + default void changeParamFuzzy(final Class newParamType, final VersionedMatcher versions) { this.changeParamFuzzy(desc(newParamType), versions); } - void changeParamFuzzy(ClassDesc newParamType, VersionedTargetedMethodMatcher versions); + void changeParamFuzzy(ClassDesc newParamType, VersionedMatcher versions); // // @@ -56,32 +66,92 @@ default void changeParamDirect(final Class newParamType, final ApiVersion api } default void changeParamDirect(final ClassDesc newParamType, final ApiVersion apiVersion, final Method staticHandler, final TargetedMethodMatcher methodMatcher) { - this.changeParamDirect(newParamType, VersionedTargetedMethodMatcher.single(apiVersion, methodMatcher, staticHandler)); + this.changeParamDirect(newParamType, VersionedMatcher.single(apiVersion, new TargetedMethodMatcherWithHandler(methodMatcher, staticHandler))); } - default void changeParamDirect(final Class newParamType, final VersionedTargetedMethodMatcher versions) { + default void changeParamDirect(final Class newParamType, final VersionedMatcher versions) { this.changeParamDirect(desc(newParamType), versions); } - void changeParamDirect(ClassDesc newParamType, VersionedTargetedMethodMatcher versions); + void changeParamDirect(ClassDesc newParamType, VersionedMatcher versions); // // - default void changeReturnTypeToSub(final Class newReturnType, final ApiVersion apiVersion, final Class legacyReturnType, final MethodMatcher methodMatcher) { - this.changeReturnTypeToSub(desc(newReturnType), apiVersion, desc(legacyReturnType), methodMatcher); + default void changeReturnTypeToSub(final Class newReturnType, final ApiVersion apiVersion, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeToSub(desc(newReturnType), apiVersion, methodMatcher); } - default void changeReturnTypeToSub(final ClassDesc newReturnType, final ApiVersion apiVersion, final ClassDesc legacyReturnType, final MethodMatcher methodMatcher) { - this.changeReturnTypeToSub(newReturnType, VersionedMethodMatcher.single(apiVersion, methodMatcher, legacyReturnType)); + default void changeReturnTypeToSub(final ClassDesc newReturnType, final ApiVersion apiVersion, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeToSub(newReturnType, VersionedMatcher.single(apiVersion, methodMatcher)); } - default void changeReturnTypeToSub(final Class newReturnType, final VersionedMethodMatcher versions) { + default void changeReturnTypeToSub(final Class newReturnType, final VersionedMatcher versions) { this.changeReturnTypeToSub(desc(newReturnType), versions); } - void changeReturnTypeToSub(ClassDesc newReturnType, VersionedMethodMatcher versions); + void changeReturnTypeToSub(ClassDesc newReturnType, VersionedMatcher versions); + // + + // + default void changeReturnTypeDirect(final Class newReturnType, final ApiVersion apiVersion, final Method staticHandler, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeDirect(desc(newReturnType), apiVersion, staticHandler, methodMatcher); + } + + default void changeReturnTypeDirect(final ClassDesc newReturnType, final ApiVersion apiVersion, final Method staticHandler, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeDirect(newReturnType, VersionedMatcher.single(apiVersion, new TargetedMethodMatcherWithHandler(methodMatcher, staticHandler))); + } + + default void changeReturnTypeDirect(final Class newReturnType, final VersionedMatcher versions) { + this.changeReturnTypeDirect(desc(newReturnType), versions); + } + + void changeReturnTypeDirect(ClassDesc newReturnType, VersionedMatcher versions); + // + + // + default void changeReturnTypeDirectWithContext(final Class newReturnType, final ApiVersion apiVersion, final Method staticHandler, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeDirectWithContext(desc(newReturnType), apiVersion, staticHandler, methodMatcher); + } + + default void changeReturnTypeDirectWithContext(final ClassDesc newReturnType, final ApiVersion apiVersion, final Method staticHandler, final TargetedMethodMatcher methodMatcher) { + this.changeReturnTypeDirectWithContext(newReturnType, VersionedMatcher.single(apiVersion, new TargetedMethodMatcherWithHandler(methodMatcher, staticHandler))); + } + + default void changeReturnTypeDirectWithContext(final Class newReturnType, final VersionedMatcher versions) { + this.changeReturnTypeDirectWithContext(desc(newReturnType), versions); + } + + void changeReturnTypeDirectWithContext(ClassDesc newReturnType, VersionedMatcher versions); // + // + default void changeFieldToMethod(final @Nullable String getterName, final @Nullable String setterName, final boolean isInterfaceMethod, final ApiVersion apiVersion, final FieldMatcher matcher) { + this.changeFieldToMethod(getterName, setterName, isInterfaceMethod, VersionedMatcher.single(apiVersion, matcher)); + } + + void changeFieldToMethod(@Nullable String getterName, @Nullable String setterName, boolean isInterfaceMethod, VersionedMatcher versions); + // + + // + default void moveInstanceMethod(final Class newOwner, final String newMethodName, final ApiVersion apiVersion, final MethodMatcher matcher) { + this.moveInstanceMethod(desc(newOwner), newMethodName, apiVersion, matcher); + } + + default void moveInstanceMethod(final ClassDesc newOwner, final String newMethodName, final ApiVersion apiVersion, final MethodMatcher matcher) { + this.moveInstanceMethod(newOwner, newMethodName, VersionedMatcher.single(apiVersion, matcher)); + } + + default void moveInstanceMethod(final Class newOwner, final String newMethodName, final VersionedMatcher versions) { + this.moveInstanceMethod(desc(newOwner), newMethodName, versions); + } + + void moveInstanceMethod(ClassDesc newOwner, String newMethodName, VersionedMatcher versions); + // + + > void addMergeableRuleFactory(NavigableMap versions); + + void addChainableRuleFactory(NavigableMap versions); + void addRuleFactory(VersionedRuleFactory factory); VersionedRuleFactory build(); diff --git a/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java b/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java index bba0029..9ec81a0 100644 --- a/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java +++ b/src/main/java/io/papermc/asm/versioned/OwnedVersionedRuleFactoryFactoryImpl.java @@ -1,15 +1,25 @@ package io.papermc.asm.versioned; +import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.field.FieldMatcher; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; +import io.papermc.asm.rules.field.FieldToMethodRewrite; +import io.papermc.asm.rules.method.DirectStaticRewrite; +import io.papermc.asm.rules.method.MoveInstanceMethod; import io.papermc.asm.rules.method.params.DirectParameterRewrite; import io.papermc.asm.rules.method.params.FuzzyParameterRewrite; import io.papermc.asm.rules.method.params.SuperTypeParamRewrite; +import io.papermc.asm.rules.method.returns.DirectReturnRewrite; import io.papermc.asm.rules.method.returns.SubTypeReturnRewrite; -import io.papermc.asm.versioned.matcher.VersionedMethodMatcher; -import io.papermc.asm.versioned.matcher.targeted.VersionedTargetedMethodMatcher; +import io.papermc.asm.versioned.matcher.TargetedMethodMatcherWithHandler; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.util.ArrayList; import java.util.List; +import java.util.NavigableMap; import java.util.Set; +import org.checkerframework.checker.nullness.qual.Nullable; public class OwnedVersionedRuleFactoryFactoryImpl implements OwnedVersionedRuleFactoryFactory { @@ -21,25 +31,60 @@ public OwnedVersionedRuleFactoryFactoryImpl(final Set owners) { } @Override - public void changeParamToSuper(final ClassDesc newParamType, final VersionedMethodMatcher versions) { + public void plainStaticRewrite(final ClassDesc newOwner, final @Nullable String staticMethodName, final VersionedMatcher versions) { + this.factories.add(new DirectStaticRewrite.Versioned(this.owners, newOwner, staticMethodName, versions)); + } + + @Override + public void changeParamToSuper(final ClassDesc newParamType, final VersionedMatcher versions) { this.factories.add(new SuperTypeParamRewrite.Versioned(this.owners, newParamType, versions)); } @Override - public void changeParamFuzzy(final ClassDesc newParamType, final VersionedTargetedMethodMatcher versions) { + public void changeParamFuzzy(final ClassDesc newParamType, final VersionedMatcher versions) { this.factories.add(new FuzzyParameterRewrite.Versioned(this.owners, newParamType, versions)); } @Override - public void changeParamDirect(final ClassDesc newParamType, final VersionedTargetedMethodMatcher versions) { + public void changeParamDirect(final ClassDesc newParamType, final VersionedMatcher versions) { this.factories.add(new DirectParameterRewrite.Versioned(this.owners, newParamType, versions)); } @Override - public void changeReturnTypeToSub(final ClassDesc newReturnType, final VersionedMethodMatcher versions) { + public void changeReturnTypeToSub(final ClassDesc newReturnType, final VersionedMatcher versions) { this.factories.add(new SubTypeReturnRewrite.Versioned(this.owners, newReturnType, versions)); } + @Override + public void changeReturnTypeDirect(final ClassDesc newReturnType, final VersionedMatcher versions) { + this.factories.add(new DirectReturnRewrite.Versioned(this.owners, newReturnType, versions, false)); + } + + @Override + public void changeReturnTypeDirectWithContext(final ClassDesc newReturnType, final VersionedMatcher versions) { + this.factories.add(new DirectReturnRewrite.Versioned(this.owners, newReturnType, versions, true)); + } + + @Override + public void changeFieldToMethod(final @Nullable String getterName, final @Nullable String setterName, final boolean isInterfaceMethod, final VersionedMatcher versions) { + this.factories.add(new FieldToMethodRewrite.Versioned(this.owners, getterName, setterName, isInterfaceMethod, versions)); + } + + @Override + public void moveInstanceMethod(final ClassDesc newOwner, final String newMethodName, final VersionedMatcher versions) { + this.factories.add(new MoveInstanceMethod.Versioned(this.owners, newOwner, newMethodName, versions)); + } + + @Override + public > void addMergeableRuleFactory(final NavigableMap versions) { + this.factories.add(new MappedVersionRuleFactory<>(versions, Mergeable::merge)); + } + + @Override + public void addChainableRuleFactory(final NavigableMap versions) { + this.factories.add(new MappedVersionRuleFactory(versions, RewriteRule::chain)); + } + @Override public void addRuleFactory(final VersionedRuleFactory factory) { this.factories.add(factory); diff --git a/src/main/java/io/papermc/asm/versioned/VersionedRuleFactory.java b/src/main/java/io/papermc/asm/versioned/VersionedRuleFactory.java index c0889d3..b311223 100644 --- a/src/main/java/io/papermc/asm/versioned/VersionedRuleFactory.java +++ b/src/main/java/io/papermc/asm/versioned/VersionedRuleFactory.java @@ -13,6 +13,10 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.Nullable; +/** + * A factory for {@link RewriteRule} that are determined + * by a {@link ApiVersion}. + */ public interface VersionedRuleFactory { VersionedRuleFactory EMPTY = apiVersion -> RewriteRule.EMPTY; diff --git a/src/main/java/io/papermc/asm/versioned/matcher/TargetedMethodMatcherWithHandler.java b/src/main/java/io/papermc/asm/versioned/matcher/TargetedMethodMatcherWithHandler.java new file mode 100644 index 0000000..1cea5ae --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/matcher/TargetedMethodMatcherWithHandler.java @@ -0,0 +1,7 @@ +package io.papermc.asm.versioned.matcher; + +import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; +import java.lang.reflect.Method; + +public record TargetedMethodMatcherWithHandler(TargetedMethodMatcher matcher, Method staticHandler) { +} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcher.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcher.java new file mode 100644 index 0000000..5eb198b --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcher.java @@ -0,0 +1,53 @@ +package io.papermc.asm.versioned.matcher; + +import io.papermc.asm.rules.RewriteRule; +import io.papermc.asm.rules.builder.matcher.field.FieldMatcher; +import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; +import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; +import io.papermc.asm.versioned.ApiVersion; +import java.util.Map; +import java.util.NavigableMap; +import java.util.TreeMap; +import java.util.function.Function; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class VersionedMatcher { + + public static VersionedMatcher single(final ApiVersion apiVersion, final C context) { + return new VersionedMatcher<>(new TreeMap<>(Map.of(apiVersion, context))); + } + + public static VersionedMatcherBuilder fieldBuilder() { + return builder(); + } + + public static VersionedMatcherBuilder methodBuilder() { + return builder(); + } + + public static VersionedMatcherBuilder targetedMethodBuilder() { + return builder(); + } + + public static VersionedMatcherBuilder builder() { + return new VersionedMatcherBuilderImpl<>(); + } + + private final NavigableMap map; + + public VersionedMatcher(final NavigableMap map) { + this.map = map; + } + + public RewriteRule ruleForVersion(final ApiVersion version, final Function creator) { + return ruleForVersion(this.map, version, creator); + } + + public static

RewriteRule ruleForVersion(final NavigableMap versions, final ApiVersion version, final Function creator) { + final Map.@Nullable Entry entry = versions.ceilingEntry(version); + if (entry == null) { + return RewriteRule.EMPTY; + } + return creator.apply(entry.getValue()); + } +} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBase.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBase.java deleted file mode 100644 index b5d8ad3..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBase.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.papermc.asm.versioned.matcher; - -import io.papermc.asm.rules.RewriteRule; -import io.papermc.asm.versioned.ApiVersion; -import java.util.Map; -import java.util.NavigableMap; -import java.util.function.Function; -import org.checkerframework.checker.nullness.qual.Nullable; - -public abstract class VersionedMatcherBase

{ - - private final NavigableMap map; - - protected VersionedMatcherBase(final NavigableMap map) { - this.map = map; - } - - public RewriteRule ruleForVersion(final ApiVersion version, final Function creator) { - return ruleForVersion(this.map, version, creator); - } - - public static

RewriteRule ruleForVersion(final NavigableMap versions, final ApiVersion version, final Function creator) { - final Map.@Nullable Entry entry = versions.ceilingEntry(version); - if (entry == null) { - return RewriteRule.EMPTY; - } - return creator.apply(entry.getValue()); - } -} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilder.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilder.java new file mode 100644 index 0000000..cc6022d --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilder.java @@ -0,0 +1,11 @@ +package io.papermc.asm.versioned.matcher; + +import io.papermc.asm.util.Builder; +import io.papermc.asm.versioned.ApiVersion; +import org.jetbrains.annotations.Contract; + +public interface VersionedMatcherBuilder extends Builder> { + + @Contract(value = "_, _ -> this", mutates = "this") + VersionedMatcherBuilder with(ApiVersion apiVersion, C context); +} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilderImpl.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilderImpl.java new file mode 100644 index 0000000..3aacff6 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMatcherBuilderImpl.java @@ -0,0 +1,24 @@ +package io.papermc.asm.versioned.matcher; + +import io.papermc.asm.versioned.ApiVersion; +import java.util.NavigableMap; +import java.util.TreeMap; + +public class VersionedMatcherBuilderImpl implements VersionedMatcherBuilder { + + protected final NavigableMap versions = new TreeMap<>(); + + @Override + public VersionedMatcherBuilder with(final ApiVersion apiVersion, final C context) { + if (this.versions.containsKey(apiVersion)) { + throw new IllegalArgumentException("Duplicate version: " + apiVersion); + } + this.versions.put(apiVersion, context); + return this; + } + + @Override + public VersionedMatcher build() { + return new VersionedMatcher<>(this.versions); + } +} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcher.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcher.java deleted file mode 100644 index c15b8d4..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcher.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.papermc.asm.versioned.matcher; - -import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; -import io.papermc.asm.versioned.ApiVersion; -import java.lang.constant.ClassDesc; -import java.util.Map; -import java.util.NavigableMap; -import java.util.TreeMap; - -public class VersionedMethodMatcher extends VersionedMatcherBase { - - public static VersionedMethodMatcherBuilder builder() { - return new VersionedMethodMatcherBuilderImpl(); - } - - public static VersionedMethodMatcher single(final ApiVersion apiVersion, final MethodMatcher matcher, final ClassDesc legacyType) { - return new VersionedMethodMatcher(new TreeMap<>(Map.of(apiVersion, new Pair(matcher, legacyType)))); - } - - VersionedMethodMatcher(final NavigableMap map) { - super(map); - } - - public record Pair(MethodMatcher matcher, ClassDesc legacyType) { - } -} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilder.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilder.java deleted file mode 100644 index 7cc7552..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilder.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.papermc.asm.versioned.matcher; - -import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; -import io.papermc.asm.util.Builder; -import io.papermc.asm.versioned.ApiVersion; -import java.lang.constant.ClassDesc; -import org.jetbrains.annotations.Contract; - -public interface VersionedMethodMatcherBuilder extends Builder { - - @Contract(value = "_, _, _ -> this", mutates = "this") - VersionedMethodMatcherBuilder with(ApiVersion apiVersion, MethodMatcher matcher, ClassDesc oldType); -} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilderImpl.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilderImpl.java deleted file mode 100644 index 9d3b2e0..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/VersionedMethodMatcherBuilderImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.papermc.asm.versioned.matcher; - -import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; -import io.papermc.asm.versioned.ApiVersion; -import java.lang.constant.ClassDesc; -import java.util.NavigableMap; -import java.util.TreeMap; - -public class VersionedMethodMatcherBuilderImpl implements VersionedMethodMatcherBuilder { - - private final NavigableMap versions = new TreeMap<>(); - - @Override - public VersionedMethodMatcherBuilder with(final ApiVersion apiVersion, final MethodMatcher matcher, final ClassDesc legacyType) { - if (this.versions.containsKey(apiVersion)) { - throw new IllegalArgumentException("Duplicate version: " + apiVersion); - } - this.versions.put(apiVersion, new VersionedMethodMatcher.Pair(matcher, legacyType)); - return this; - } - - @Override - public VersionedMethodMatcher build() { - return new VersionedMethodMatcher(this.versions); - } -} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilder.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilder.java new file mode 100644 index 0000000..0b2f0c2 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilder.java @@ -0,0 +1,16 @@ +package io.papermc.asm.versioned.matcher; + +import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; +import io.papermc.asm.versioned.ApiVersion; +import java.lang.reflect.Method; +import org.jetbrains.annotations.Contract; + +public interface VersionedTargetedMethodMatcherWithHandlerBuilder extends VersionedMatcherBuilder { + + @Contract(value = "_, _, _ -> this", mutates = "this") + VersionedTargetedMethodMatcherWithHandlerBuilder with(ApiVersion apiVersion, TargetedMethodMatcher matcher, Method staticHandler); + + @Override + VersionedTargetedMethodMatcherWithHandlerBuilder with(ApiVersion apiVersion, TargetedMethodMatcherWithHandler context); + +} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilderImpl.java b/src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilderImpl.java new file mode 100644 index 0000000..28d1c97 --- /dev/null +++ b/src/main/java/io/papermc/asm/versioned/matcher/VersionedTargetedMethodMatcherWithHandlerBuilderImpl.java @@ -0,0 +1,20 @@ +package io.papermc.asm.versioned.matcher; + +import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; +import io.papermc.asm.versioned.ApiVersion; +import java.lang.reflect.Method; + +public final class VersionedTargetedMethodMatcherWithHandlerBuilderImpl + extends VersionedMatcherBuilderImpl + implements VersionedTargetedMethodMatcherWithHandlerBuilder { + + @Override + public VersionedTargetedMethodMatcherWithHandlerBuilder with(final ApiVersion apiVersion, final TargetedMethodMatcherWithHandler context) { + return (VersionedTargetedMethodMatcherWithHandlerBuilder) super.with(apiVersion, context); + } + + @Override + public VersionedTargetedMethodMatcherWithHandlerBuilder with(final ApiVersion apiVersion, final TargetedMethodMatcher matcher, final Method staticHandler) { + return this.with(apiVersion, new TargetedMethodMatcherWithHandler(matcher, staticHandler)); + } +} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcher.java b/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcher.java deleted file mode 100644 index d4749e8..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcher.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.papermc.asm.versioned.matcher.targeted; - -import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; -import io.papermc.asm.versioned.ApiVersion; -import io.papermc.asm.versioned.matcher.VersionedMatcherBase; -import java.lang.reflect.Method; -import java.util.Map; -import java.util.NavigableMap; -import java.util.TreeMap; - -public final class VersionedTargetedMethodMatcher extends VersionedMatcherBase { - - public static VersionedTargetedMethodMatcherBuilder builder() { - return new VersionedTargetedMethodMatcherBuilderImpl(); - } - - public static VersionedTargetedMethodMatcher single(final ApiVersion apiVersion, final TargetedMethodMatcher matcher, final Method staticHandler) { - return new VersionedTargetedMethodMatcher(new TreeMap<>(Map.of(apiVersion, new Pair(matcher, staticHandler)))); - } - - VersionedTargetedMethodMatcher(final NavigableMap map) { - super(map); - } - - public record Pair(TargetedMethodMatcher matcher, Method staticHandler) { - } -} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilder.java b/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilder.java deleted file mode 100644 index 2634642..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilder.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.papermc.asm.versioned.matcher.targeted; - -import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; -import io.papermc.asm.util.Builder; -import io.papermc.asm.versioned.ApiVersion; -import java.lang.reflect.Method; -import org.jetbrains.annotations.Contract; - -public interface VersionedTargetedMethodMatcherBuilder extends Builder { - - @Contract(value = "_, _, _ -> this", mutates = "this") - VersionedTargetedMethodMatcherBuilder with(ApiVersion apiVersion, TargetedMethodMatcher matcher, Method staticHandler); -} diff --git a/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilderImpl.java b/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilderImpl.java deleted file mode 100644 index 2b7b53c..0000000 --- a/src/main/java/io/papermc/asm/versioned/matcher/targeted/VersionedTargetedMethodMatcherBuilderImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.papermc.asm.versioned.matcher.targeted; - -import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; -import io.papermc.asm.versioned.ApiVersion; -import java.lang.reflect.Method; -import java.util.NavigableMap; -import java.util.TreeMap; - -public class VersionedTargetedMethodMatcherBuilderImpl implements VersionedTargetedMethodMatcherBuilder { - - private final NavigableMap versions = new TreeMap<>(); - - @Override - public VersionedTargetedMethodMatcherBuilder with(final ApiVersion apiVersion, final TargetedMethodMatcher matcher, final Method staticHandler) { - if (this.versions.containsKey(apiVersion)) { - throw new IllegalArgumentException("Duplicate version: " + apiVersion); - } - this.versions.put(apiVersion, new VersionedTargetedMethodMatcher.Pair(matcher, staticHandler)); - return this; - } - - @Override - public VersionedTargetedMethodMatcher build() { - return new VersionedTargetedMethodMatcher(this.versions); - } -} diff --git a/src/test/java/io/papermc/asm/ApiVersion.java b/src/test/java/io/papermc/asm/ApiVersion.java index cb7606f..f9067c7 100644 --- a/src/test/java/io/papermc/asm/ApiVersion.java +++ b/src/test/java/io/papermc/asm/ApiVersion.java @@ -1,12 +1,11 @@ package io.papermc.asm; import java.util.List; -import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.framework.qual.DefaultQualifier; @DefaultQualifier(NonNull.class) -public class ApiVersion implements io.papermc.asm.versioned.ApiVersion { +public record ApiVersion(int version) implements io.papermc.asm.versioned.ApiVersion { public static final ApiVersion ONE = new ApiVersion(1); public static final ApiVersion TWO = new ApiVersion(2); @@ -15,34 +14,8 @@ public class ApiVersion implements io.papermc.asm.versioned.ApiVersion { public static final List ALL_VERSIONS = List.of(ONE, TWO, THREE, FOUR); - private final int version; - - public ApiVersion(final int version) { - this.version = version; - } - @Override public int compareTo(final io.papermc.asm.versioned.ApiVersion o) { return Integer.compare(this.version, ((ApiVersion) o).version); } - - @Override - public String toString() { - return "ApiVersion{" + - "version=" + this.version + - '}'; - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || this.getClass() != o.getClass()) return false; - final ApiVersion that = (ApiVersion) o; - return this.version == that.version; - } - - @Override - public int hashCode() { - return Objects.hashCode(this.version); - } } diff --git a/src/test/java/io/papermc/asm/rules/methods/MethodRewritesTest.java b/src/test/java/io/papermc/asm/rules/methods/MethodRewritesTest.java index 223e0bc..7b3c37c 100644 --- a/src/test/java/io/papermc/asm/rules/methods/MethodRewritesTest.java +++ b/src/test/java/io/papermc/asm/rules/methods/MethodRewritesTest.java @@ -7,13 +7,13 @@ import io.papermc.asm.TransformerTest; import io.papermc.asm.checks.TransformerCheck; import io.papermc.asm.rules.RewriteRule; -import io.papermc.asm.rules.builder.matcher.method.MethodMatcher; -import io.papermc.asm.rules.builder.matcher.method.MethodMatcherBuilder; +import io.papermc.asm.rules.builder.matcher.method.MethodTypeMatcherBuilder; +import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher; import io.papermc.asm.rules.method.params.SuperTypeParamRewrite; import io.papermc.asm.rules.method.returns.SubTypeReturnRewrite; import io.papermc.asm.versioned.VersionedRuleFactory; import io.papermc.asm.versioned.VersionedTester; -import io.papermc.asm.versioned.matcher.VersionedMethodMatcher; +import io.papermc.asm.versioned.matcher.VersionedMatcher; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.util.Map; @@ -29,12 +29,11 @@ class MethodRewritesTest { void testSuperTypeParam(final TransformerCheck check) { final RewriteRule rule = RewriteRule.forOwnerClass(Methods.class, builder -> { builder.changeParamToSuper( - Player.class, Entity.class, - MethodMatcher.builder() + TargetedMethodMatcher.builder() .match("consume", b -> b.virtual()) .match("consumeStatic", b -> b.statik()) - .hasParam(PLAYER) + .targetParam(PLAYER) .build() ); }); @@ -44,12 +43,15 @@ void testSuperTypeParam(final TransformerCheck check) { @Test void testVersionedSuperTypeParam() { final VersionedRuleFactory factory = VersionedRuleFactory.forOwnerClass(String.class, builder -> { - final MethodMatcher method1 = MethodMatcher.builder().match("method1").build(); + final TargetedMethodMatcher method1 = TargetedMethodMatcher.builder() + .match("method1").targetParam(ConstantDescs.CD_int).build(); + final TargetedMethodMatcher method3 = TargetedMethodMatcher.builder() + .match("method1").targetParam(ConstantDescs.CD_long).build(); builder.changeParamToSuper( String.class, - VersionedMethodMatcher.builder() - .with(ApiVersion.ONE, method1, ConstantDescs.CD_int) - .with(ApiVersion.THREE, method1, ConstantDescs.CD_long) + VersionedMatcher.targetedMethodBuilder() + .with(ApiVersion.ONE, method1) + .with(ApiVersion.THREE, method3) .build() ); }); @@ -65,12 +67,11 @@ void testVersionedSuperTypeParam() { void testSubTypeReturn(final TransformerCheck check) { final RewriteRule rule = RewriteRule.forOwnerClass(Methods.class, builder -> { builder.changeReturnTypeToSub( - Entity.class, Player.class, - MethodMatcher.builder() - .match("get", MethodMatcherBuilder.MatchBuilder::virtual) - .match("getStatic", MethodMatcherBuilder.MatchBuilder::statik) - .hasReturn(ENTITY) + TargetedMethodMatcher.builder() + .match("get", MethodTypeMatcherBuilder::virtual) + .match("getStatic", MethodTypeMatcherBuilder::statik) + .targetReturn(ENTITY) .build() ); }); @@ -80,12 +81,15 @@ void testSubTypeReturn(final TransformerCheck check) { @Test void testVersionedSubTypeReturn() { final VersionedRuleFactory factory = VersionedRuleFactory.forOwnerClass(String.class, builder -> { - final MethodMatcher method1 = MethodMatcher.builder().match("method1").build(); + final TargetedMethodMatcher method1 = TargetedMethodMatcher.builder() + .match("method1").targetReturn(ConstantDescs.CD_int).build(); + final TargetedMethodMatcher method3 = TargetedMethodMatcher.builder() + .match("method1").targetReturn(ConstantDescs.CD_long).build(); builder.changeReturnTypeToSub( String.class, - VersionedMethodMatcher.builder() - .with(ApiVersion.ONE, method1, ConstantDescs.CD_int) - .with(ApiVersion.THREE, method1, ConstantDescs.CD_long) + VersionedMatcher.targetedMethodBuilder() + .with(ApiVersion.ONE, method1) + .with(ApiVersion.THREE, method3) .build() ); }); 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 b3cdbc3..297e3e3 100644 --- a/src/test/java/io/papermc/asm/rules/rename/RenameRuleTest.java +++ b/src/test/java/io/papermc/asm/rules/rename/RenameRuleTest.java @@ -5,6 +5,7 @@ import io.papermc.asm.ApiVersion; import io.papermc.asm.TransformerTest; import io.papermc.asm.checks.TransformerCheck; +import io.papermc.asm.versioned.MappedVersionRuleFactory; import io.papermc.asm.versioned.VersionedRuleFactory; import java.lang.constant.ClassDesc; import java.util.HashMap; @@ -62,18 +63,29 @@ void testVersionedRenamerRule() { .build() ); - final VersionedRuleFactory factory = new RenameRule.Versioned(new TreeMap<>(versions)); + final VersionedRuleFactory factory = MappedVersionRuleFactory.mergeable(new TreeMap<>(versions)); final RenameRule ruleOne = (RenameRule) factory.createRule(ApiVersion.ONE); final RenameRule ruleTwo = (RenameRule) factory.createRule(ApiVersion.TWO); - assertEquals(method("single").apply(ruleOne), "value"); - assertEquals(method("newValue").apply(ruleOne), "value"); + assertEquals("value", annotationMethod("single").apply(ruleOne)); + assertEquals("value", annotationMethod("newValue").apply(ruleOne)); - assertNull(method("single").apply(ruleTwo)); - assertEquals(method("newValue").apply(ruleTwo), "value"); + assertNull(annotationMethod("single").apply(ruleTwo)); + assertEquals("value", annotationMethod("newValue").apply(ruleTwo)); + assertEquals("ONE", enumField("A").apply(ruleOne)); + assertEquals("ONE", enumField("OTHER_A").apply(ruleOne)); + assertEquals("TWO", enumField("B").apply(ruleOne)); + + assertEquals("ONE", enumField("OTHER_A").apply(ruleTwo)); + assertNull(enumField("A").apply(ruleTwo)); + assertEquals("TWO", enumField("B").apply(ruleTwo)); } - private static Function method(final String legacyName) { + private static Function annotationMethod(final String legacyName) { return renameRule -> renameRule.renames().get("%s.%s%s".formatted("data/types/rename/TestAnnotation", legacyName, "()Ldata/types/rename/TestEnum;")); } + + private static Function enumField(final String legacyName) { + return renameRule -> renameRule.enumFieldRenames().get(TEST_ENUM).fieldRenames().get(legacyName); + } }