Skip to content

Commit

Permalink
add more utility methods/functionality
Browse files Browse the repository at this point in the history
mainly just qol stuff I found when re-implementing
Commodore
  • Loading branch information
Machine-Maker committed Sep 28, 2024
1 parent 578117b commit faa3e98
Show file tree
Hide file tree
Showing 20 changed files with 169 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ private DefineClassRule(final String proxyClassName, final boolean assumeClassLo
this.assumeClassLoader = assumeClassLoader;
}

@Override
public boolean shouldProcess(final ClassProcessingContext context, final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface, final boolean isInvokeDynamic) {
return true; // see comment below
}

// We could split this into multiple rules and return false for shouldProcess when the processing class doesn't
// extend (S)CL. However since the MethodHandles.Lookup portion always needs to run, the actual benefit would
// be beyond minute (if not actually worse).
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/papermc/asm/rules/builder/RuleFactory.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.papermc.asm.rules.builder;

import io.papermc.asm.rules.RewriteRule;
import io.papermc.asm.rules.builder.matcher.field.FieldMatcher;
import io.papermc.asm.rules.builder.matcher.field.FieldMatcherBuilder;
import io.papermc.asm.rules.builder.matcher.method.MethodMatcher;
import io.papermc.asm.rules.builder.matcher.method.targeted.TargetedMethodMatcher;
import java.lang.constant.ClassDesc;
Expand All @@ -28,6 +28,8 @@ static RuleFactory.Factory combine(final RuleFactory.Factory... factories) {

void plainStaticRewrite(ClassDesc newOwner, MethodMatcher methodMatcher);

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);
Expand Down Expand Up @@ -70,7 +72,7 @@ default void changeReturnTypeDirectWithContext(final Class<?> newReturnType, fin

void changeReturnTypeDirectWithContext(ClassDesc newReturnType, Method staticHandler, TargetedMethodMatcher targetedMethodMatcher);

void changeFieldToMethod(@Nullable String getterName, @Nullable String setterName, boolean isInterfaceMethod, Consumer<? super FieldMatcher.Builder> builderConsumer);
void changeFieldToMethod(@Nullable String getterName, @Nullable String setterName, boolean isInterfaceMethod, Consumer<? super FieldMatcherBuilder> builderConsumer);

default void moveInstanceMethod(final Class<?> newOwner, final String newMethodName, final MethodMatcher methodMatcher) {
this.moveInstanceMethod(desc(newOwner), newMethodName, methodMatcher);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.papermc.asm.rules.RewriteRule;
import io.papermc.asm.rules.builder.matcher.field.FieldMatcher;
import io.papermc.asm.rules.builder.matcher.field.FieldMatcherBuilder;
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;
Expand Down Expand Up @@ -43,6 +44,11 @@ public void plainStaticRewrite(final ClassDesc newOwner, final MethodMatcher met
this.addRule(new DirectStaticRewrite(this.owners, methodMatcher, newOwner));
}

@Override
public void plainStaticRewrite(final ClassDesc newOwner, final MethodMatcher methodMatcher, final String staticMethodName) {
this.addRule(new DirectStaticRewrite(this.owners, staticMethodName, methodMatcher, newOwner));
}

@Override
public void changeParamToSuper(final ClassDesc legacyParamType, final ClassDesc newParamType, final MethodMatcher methodMatcher) {
this.addRule(new SuperTypeParamRewrite(this.owners, methodMatcher, legacyParamType, newParamType));
Expand Down Expand Up @@ -78,7 +84,7 @@ private void changeReturnTypeDirect(final ClassDesc newReturnType, final Method
}

@Override
public void changeFieldToMethod(final @Nullable String getterName, final @Nullable String setterName, final boolean isInterfaceMethod, final Consumer<? super FieldMatcher.Builder> builderConsumer) {
public void changeFieldToMethod(final @Nullable String getterName, final @Nullable String setterName, final boolean isInterfaceMethod, final Consumer<? super FieldMatcherBuilder> builderConsumer) {
this.addRule(new FieldToMethodRewrite(this.owners, build(builderConsumer, FieldMatcher::builder), getterName, setterName, isInterfaceMethod));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,16 @@
package io.papermc.asm.rules.builder.matcher.field;

import java.lang.constant.ClassDesc;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

import static java.util.function.Predicate.isEqual;

@FunctionalInterface
public interface FieldMatcher {

boolean matchesName(String name);
static FieldMatcherBuilderImpl builder() {
return new FieldMatcherBuilderImpl();
}

boolean matches(String name, String descriptor);

static Builder builder() {
return new Builder();
default FieldMatcher or(final FieldMatcher other) {
return (name, descriptor) -> this.matches(name, descriptor) || other.matches(name, descriptor);
}

final class Builder implements io.papermc.asm.util.Builder<FieldMatcher> {

private Predicate<String> byName = $ -> false;
private Predicate<? super ClassDesc> byType = $ -> true;

private Builder() {
}

public Builder names(final String... names) {
return this.names(List.of(names));
}

public Builder names(final Collection<String> names) {
this.byName = this.byName.or(names::contains);
return this;
}

public Builder desc(final ClassDesc desc) {
return this.desc(isEqual(desc));
}

public Builder desc(final Predicate<? super ClassDesc> predicate) {
this.byType = predicate;
return this;
}

@Override
public FieldMatcher build() {
return new FieldMatcherImpl(this.byName, this.byType);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.papermc.asm.rules.builder.matcher.field;

import io.papermc.asm.util.Builder;
import java.lang.constant.ClassDesc;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;

import static java.util.function.Predicate.isEqual;

public interface FieldMatcherBuilder extends Builder<FieldMatcher> {

default FieldMatcherBuilder match(final String fieldName) {
return this.match(fieldName, $ -> true);
}

default FieldMatcherBuilder match(final String fieldName, final ClassDesc fieldDesc) {
return this.match(fieldName, isEqual(fieldDesc));
}

default FieldMatcherBuilder match(final String fieldName, final Predicate<ClassDesc> fieldDescPredicate) {
return this.match(isEqual(fieldName), fieldDescPredicate);
}

default FieldMatcherBuilder match(final Collection<String> fieldNames) {
return this.match(fieldNames, $ -> true);
}

default FieldMatcherBuilder match(final Collection<String> fieldNames, final ClassDesc fieldDesc) {
return this.match(fieldNames, isEqual(fieldDesc));
}

default FieldMatcherBuilder match(final Collection<String> fieldNames, final Predicate<ClassDesc> fieldDescPredicate) {
final List<String> copy = List.copyOf(fieldNames);
return this.match(copy::contains, fieldDescPredicate);
}

default FieldMatcherBuilder match(final Predicate<String> fieldNamePredicate) {
return this.match(fieldNamePredicate, $ -> true);
}

default FieldMatcherBuilder match(final Predicate<String> fieldNamePredicate, final ClassDesc fieldDesc) {
return this.match(fieldNamePredicate, isEqual(fieldDesc));
}

FieldMatcherBuilder match(Predicate<String> fieldNamePredicate, Predicate<ClassDesc> fieldDescPredicate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.papermc.asm.rules.builder.matcher.field;

import java.lang.constant.ClassDesc;
import java.util.function.Predicate;

public final class FieldMatcherBuilderImpl implements FieldMatcherBuilder {

private FieldMatcher matcher = (name, descriptor) -> false;

FieldMatcherBuilderImpl() {
}

@Override
public FieldMatcherBuilder match(final Predicate<String> fieldNamePredicate, final Predicate<ClassDesc> fieldDescPredicate) {
this.matcher = this.matcher.or((name, descriptor) -> fieldNamePredicate.test(name) && fieldDescPredicate.test(ClassDesc.ofDescriptor(descriptor)));
return this;
}

@Override
public FieldMatcher build() {
return this.matcher;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ public FieldMatcherImpl(final Predicate<? super String> byName, final Predicate<
this.byDesc = byDesc;
}

@Override
public boolean matchesName(final String name) {
return this.byName.test(name);
}

@Override
public boolean matches(final String name, final String descriptor) {
return this.byName.test(name) && this.byDesc.test(classDesc(descriptor));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.papermc.asm.rules.builder.matcher.method;

import java.lang.constant.MethodTypeDesc;
import java.util.function.Consumer;
import java.util.function.Predicate;

@FunctionalInterface
Expand All @@ -10,6 +11,10 @@ static MethodMatcherBuilder builder() {
return new MethodMatcherBuilderImpl();
}

static MethodMatcher single(final String name, final Consumer<MethodMatcherBuilder.MatchBuilder> matchBuilderConsumer) {
return builder().match(name, matchBuilderConsumer).build();
}

boolean matches(int opcode, boolean isInvokeDynamic, String name, String descriptor);

default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private static MethodMatcher createStaticMatcher(final ClassDesc legacyEnumType)
.build();
}

final class EnumVirtualMethods implements GeneratedStaticRewrite {
final class EnumVirtualMethods implements GeneratedStaticRewrite, OwnableMethodRewriteRule.Filtered {

@Override
public MethodMatcher methodMatcher() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,12 @@ public void visitFieldInsn(final int opcode, final String owner, final String na
interface Rewrite {
void apply(MethodVisitor delegate);
}

record SimpleRewrite(int opcode, String owner, String name, ClassDesc fieldTypeDesc) implements Rewrite {

@Override
public void apply(final MethodVisitor delegate) {
delegate.visitFieldInsn(this.opcode, this.owner, this.name, this.fieldTypeDesc.descriptorString());
}
}
}
27 changes: 13 additions & 14 deletions src/main/java/io/papermc/asm/rules/field/FieldToMethodRewrite.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,20 @@ MethodTypeDesc desc(final ClassDesc fieldTypeDesc) {
}

@Override
public Rewrite rewrite(final ClassProcessingContext context, final int opcode, final String owner, final String name, final ClassDesc fieldTypeDesc) {
public @Nullable Rewrite rewrite(final ClassProcessingContext context, final int opcode, final String owner, final String name, final ClassDesc fieldTypeDesc) {
final Type type = switch (opcode) {
case Opcodes.GETFIELD, Opcodes.GETSTATIC -> Type.GETTER;
case Opcodes.PUTFIELD, Opcodes.PUTSTATIC -> Type.SETTER;
default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode);
};
final @Nullable String methodName = switch (type) {
case GETTER -> this.getterName;
case SETTER -> this.setterName;
};
if (methodName == null) {
return null;
}
return (delegate) -> {
final Type type = switch (opcode) {
case Opcodes.GETFIELD, Opcodes.GETSTATIC -> Type.GETTER;
case Opcodes.PUTFIELD, Opcodes.PUTSTATIC -> Type.SETTER;
default -> throw new IllegalArgumentException("Unexpected opcode: " + opcode);
};
final @Nullable String methodName = switch (type) {
case GETTER -> this.getterName;
case SETTER -> this.setterName;
};
if (methodName == null) {
return;
}

delegate.visitMethodInsn(type.opcode(opcode), owner, methodName, type.desc(fieldTypeDesc).descriptorString(), this.isInterfaceMethod);
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,17 @@

import io.papermc.asm.ClassProcessingContext;
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 java.lang.constant.ClassDesc;
import java.lang.constant.MethodTypeDesc;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.Nullable;

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

/**
* Rewrites a method by just directly routing it to an identical static method in another class.
Expand All @@ -12,7 +21,29 @@
* @param methodMatcher the method matcher to use
* @param staticRedirectOwner the owner to redirect to
*/
public record DirectStaticRewrite(Set<ClassDesc> owners, MethodMatcher methodMatcher, ClassDesc staticRedirectOwner) implements StaticRewrite {
public record DirectStaticRewrite(Set<ClassDesc> owners, @Nullable String staticMethodName, MethodMatcher methodMatcher, ClassDesc staticRedirectOwner) implements StaticRewrite, OwnableMethodRewriteRule.Filtered {

public DirectStaticRewrite(final Set<ClassDesc> owners, final MethodMatcher methodMatcher, final ClassDesc staticRedirectOwner) {
this(owners, null, methodMatcher, staticRedirectOwner);
}

@Override
public MethodRewrite<GeneratedMethodHolder.MethodCallData> createRewrite(final ClassProcessingContext context, final MethodTypeDesc intermediateDescriptor, final GeneratedMethodHolder.MethodCallData originalCallData) {
if (this.staticMethodName() == null) {
return StaticRewrite.super.createRewrite(context, intermediateDescriptor, originalCallData);
} else {
return new SimpleRewrite(staticOp(originalCallData.isInvokeDynamic()), this.staticRedirectOwner(context), this.staticMethodName(), this.transformToRedirectDescriptor(intermediateDescriptor), false, originalCallData.isInvokeDynamic());
}
}

@Override
public MethodRewrite<GeneratedMethodHolder.ConstructorCallData> createConstructorRewrite(final ClassProcessingContext context, final MethodTypeDesc intermediateDescriptor, final GeneratedMethodHolder.ConstructorCallData originalCallData) {
if (this.staticMethodName() == null) {
return StaticRewrite.super.createConstructorRewrite(context, intermediateDescriptor, originalCallData);
} else {
return new ConstructorRewrite(this.staticRedirectOwner(context), toOwner(originalCallData.owner()), this.staticMethodName(), this.transformToRedirectDescriptor(intermediateDescriptor));
}
}

@Override
public ClassDesc staticRedirectOwner(final ClassProcessingContext context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,7 @@ public interface MethodRewriteRule extends RewriteRule {
* @param isInvokeDynamic if the method call is from an invokedynamic instruction
* @return true to continue processing the instruction
*/
default boolean shouldProcess(final ClassProcessingContext context, final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface, final boolean isInvokeDynamic) {
return true;
}
boolean shouldProcess(final ClassProcessingContext context, final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface, final boolean isInvokeDynamic);

/**
* Creates a {@link MethodRewrite} which will be applied to the processed bytecode.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public record MoveInstanceMethod(
MethodMatcher methodMatcher,
ClassDesc newOwner,
String newMethodName
) implements GeneratedStaticRewrite {
) implements GeneratedStaticRewrite, OwnableMethodRewriteRule.Filtered {

@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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import static io.papermc.asm.util.OpcodeUtils.isVirtual;
import static io.papermc.asm.util.OpcodeUtils.staticOp;

public interface StaticRewrite extends OwnableMethodRewriteRule.Filtered {
public interface StaticRewrite extends MethodRewriteRule {

String CONSTRUCTOR_METHOD_NAME = "<init>";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ public interface TargetedTypeGeneratedStaticRewrite extends GeneratedStaticRewri
*/
ClassDesc existingType();

/**
* Gets the targeted method matcher for the rewrite.
*
* @return the targeted method matcher
*/
TargetedMethodMatcher methodMatcher();

interface Parameter extends TargetedTypeGeneratedStaticRewrite, StaticHandlerGeneratedMethodHolder.Param {

/**
* Gets the targeted method matcher for the rewrite.
*
* @return the targeted method matcher
*/
TargetedMethodMatcher methodMatcher();

@Override
default MethodTypeDesc transformInvokedDescriptor(final MethodTypeDesc original, final Set<Integer> context) {
// To create the generated method descriptor from the existing (in source) descriptor, we replace the
Expand Down
Loading

0 comments on commit faa3e98

Please sign in to comment.