Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support versioning each rewrite rule #32

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ val filtered = tasks.register<FilterTestClasspath>("filteredTestClasspath") {

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

testDataNewTargets.implementationConfigurationName(mainForNewTargets.output)
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/io/papermc/asm/rules/RewriteRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<? extends RewriteRule> rules) {
final List<? extends RewriteRule> filteredRules = rules.stream().filter(r -> r != EMPTY).toList();
if (filteredRules.isEmpty()) {
Expand Down
18 changes: 6 additions & 12 deletions src/main/java/io/papermc/asm/rules/builder/RuleFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/io/papermc/asm/rules/field/FieldToMethodRewrite.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<ClassDesc> owners, @Nullable String getterName, @Nullable String setterName, boolean isInterfaceMethod, VersionedMatcher<FieldMatcher> 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()));
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/io/papermc/asm/rules/method/DirectStaticRewrite.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -49,4 +53,12 @@ public MethodRewrite<GeneratedMethodHolder.ConstructorCallData> createConstructo
public ClassDesc staticRedirectOwner(final ClassProcessingContext context) {
return this.staticRedirectOwner;
}

public record Versioned(Set<ClassDesc> owners, ClassDesc staticRedirectOwner, @Nullable String staticMethodName, VersionedMatcher<MethodMatcher> 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()));
}
}
}
12 changes: 12 additions & 0 deletions src/main/java/io/papermc/asm/rules/method/MoveInstanceMethod.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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<ClassDesc> owners, ClassDesc newOwner, String newMethodName, VersionedMatcher<MethodMatcher> 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()));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package io.papermc.asm.rules.method.params;

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;
Expand All @@ -17,4 +22,12 @@
* @param staticHandler the method which will be used to convert the legacy type to the new type
*/
public record DirectParameterRewrite(Set<ClassDesc> owners, ClassDesc existingType, TargetedMethodMatcher methodMatcher, Method staticHandler) implements TargetedTypeGeneratedStaticRewrite.Parameter, OwnableMethodRewriteRule.Filtered {

public record Versioned(Set<ClassDesc> owners, ClassDesc existingType, VersionedMatcher<TargetedMethodMatcherWithHandler> versions) implements VersionedRuleFactory {

@Override
public RewriteRule createRule(final ApiVersion apiVersion) {
return this.versions.ruleForVersion(apiVersion, pair -> new DirectParameterRewrite(this.owners, this.existingType, pair.matcher(), pair.staticHandler()));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package io.papermc.asm.rules.method.params;

import io.papermc.asm.ClassProcessingContext;
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.rules.method.rewrite.MethodRewrite;
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.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
Expand Down Expand Up @@ -54,4 +59,12 @@ public MethodRewrite<MethodCallData> createRewrite(final ClassProcessingContext
arguments[MethodRewrite.DYNAMIC_TYPE_IDX] = Type.getMethodType(newDynamicMethodType.descriptorString());
});
}

public record Versioned(Set<ClassDesc> owners, ClassDesc existingType, VersionedMatcher<TargetedMethodMatcherWithHandler> versions) implements VersionedRuleFactory {

@Override
public RewriteRule createRule(final ApiVersion apiVersion) {
return this.versions.ruleForVersion(apiVersion, pair -> new FuzzyParameterRewrite(this.owners, this.existingType, pair.matcher(), pair.staticHandler()));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.papermc.asm.rules.method.params;

import io.papermc.asm.ClassProcessingContext;
import io.papermc.asm.rules.builder.matcher.method.MethodMatcher;
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.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;
Expand All @@ -17,18 +21,29 @@
* 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<ClassDesc> owners, MethodMatcher methodMatcher, ClassDesc oldParamType, ClassDesc newParamType) implements OwnableMethodRewriteRule.Filtered {
public record SuperTypeParamRewrite(Set<ClassDesc> 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) {
return new SimpleRewrite(opcode, owner, name, this.modifyMethodDescriptor(descriptor), isInterface, isInvokeDynamic);
}

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<ClassDesc> owners, ClassDesc newParamType, VersionedMatcher<TargetedMethodMatcher> versions) implements VersionedRuleFactory {

@Override
public RewriteRule createRule(final ApiVersion apiVersion) {
return this.versions.ruleForVersion(apiVersion, matcher -> new SuperTypeParamRewrite(this.owners(), matcher, this.newParamType()));
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -30,4 +35,12 @@ public record DirectReturnRewrite(Set<ClassDesc> owners, ClassDesc existingType,
throw new IllegalArgumentException("staticHandler param type isn't " + existingType);
}
}

public record Versioned(Set<ClassDesc> owners, ClassDesc existingType, VersionedMatcher<TargetedMethodMatcherWithHandler> 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()));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package io.papermc.asm.rules.method.returns;

import io.papermc.asm.ClassProcessingContext;
import io.papermc.asm.rules.builder.matcher.method.MethodMatcher;
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.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;
Expand All @@ -15,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<ClassDesc> owners, MethodMatcher methodMatcher, ClassDesc oldReturnType, ClassDesc newReturnType) implements OwnableMethodRewriteRule.Filtered {
public record SubTypeReturnRewrite(Set<ClassDesc> 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;
Expand All @@ -32,4 +39,12 @@ public record SubTypeReturnRewrite(Set<ClassDesc> owners, MethodMatcher methodMa
private MethodTypeDesc modifyMethodDescriptor(final MethodTypeDesc methodDescriptor) {
return methodDescriptor.changeReturnType(this.newReturnType());
}

public record Versioned(Set<ClassDesc> owners, ClassDesc newReturnType, VersionedMatcher<TargetedMethodMatcher> versions) implements VersionedRuleFactory {

@Override
public RewriteRule createRule(final ApiVersion apiVersion) {
return this.versions.ruleForVersion(apiVersion, matcher -> new SubTypeReturnRewrite(this.owners(), matcher, this.newReturnType()));
}
}
}
Loading
Loading