Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
java: [ 17 ]
java: [ 21 ]
fail-fast: true
steps:
- uses: actions/checkout@v6
Expand All @@ -37,13 +37,13 @@ jobs:
if: "${{ env.STATUS != 'release' && github.event_name == 'push' && github.ref == 'refs/heads/main' }}"
run: ./gradlew publish
env:
ORG_GRADLE_PROJECT_paperSnapshotsUsername: "${{ secrets.PAPER_REPO_USER }}"
ORG_GRADLE_PROJECT_paperSnapshotsPassword: "${{ secrets.PAPER_REPO_PASSWORD }}"
ORG_GRADLE_PROJECT_paperSnapshotsUsername: "${{ secrets.ARTIFACTORY_USERNAME }}"
ORG_GRADLE_PROJECT_paperSnapshotsPassword: "${{ secrets.ARTIFACTORY_PASSWORD }}"
- name: Publish Release
if: "${{ env.STATUS == 'release' && github.event_name == 'release' }}"
run: ./gradlew publish
env:
ORG_GRADLE_PROJECT_paperReleasesUsername: "${{ secrets.PAPER_REPO_USER }}"
ORG_GRADLE_PROJECT_paperReleasesPassword: "${{ secrets.PAPER_REPO_PASSWORD }}"
ORG_GRADLE_PROJECT_paperReleasesUsername: "${{ secrets.ARTIFACTORY_USERNAME }}"
ORG_GRADLE_PROJECT_paperReleasesPassword: "${{ secrets.ARTIFACTORY_PASSWORD }}"
ORG_GRADLE_PROJECT_signingKey: "${{ secrets.SIGNING_KEY }}"
ORG_GRADLE_PROJECT_signingPassword: "${{ secrets.SIGNING_PASSWORD }}"
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ allprojects {

indra {
javaVersions {
target(17)
target(21)
strictVersions(true)
}

publishSnapshotsTo("paperSnapshots", "https://repo.papermc.io/repository/maven-snapshots/")
publishReleasesTo("paperReleases", "https://repo.papermc.io/repository/maven-releases/")
publishSnapshotsTo("paperSnapshots", "https://artifactory.papermc.io/artifactory/snapshots/")
publishReleasesTo("paperReleases", "https://artifactory.papermc.io/artifactory/releases/")
signWithKeyFromProperties("signingKey", "signingPassword")

apache2License()
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
asm = "9.8"
asm = "9.9.1"
junit = "5.13.4"
junitPlatform = "1.13.4"
indra = "3.2.0"
Expand Down
78 changes: 78 additions & 0 deletions src/main/java/io/papermc/asm/rules/RuleScanner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.papermc.asm.rules;

import io.papermc.asm.rules.method.DirectStaticRewrite;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.jetbrains.annotations.ApiStatus;

public final class RuleScanner {

private static final Map<Class<? extends Annotation>, AnnotatedMethodFactory<?, ?>> INTERNAL_FACTORIES = new HashMap<>();

static {
register(DirectStaticRewrite.Wrapper.class, DirectStaticRewrite::create);
}

public static final Map<Class<? extends Annotation>, AnnotatedMethodFactory<?, ?>> FACTORIES = Collections.unmodifiableMap(INTERNAL_FACTORIES);

private static <A extends Annotation> void register(final Class<A> annotationClass, final AnnotatedMethodFactory<A, ? extends RewriteRule> factory) {
if (INTERNAL_FACTORIES.containsKey(annotationClass)) {
throw new IllegalArgumentException("Factory for " + annotationClass.getName() + " is already registered");
}
INTERNAL_FACTORIES.put(annotationClass, factory);
}

public static RewriteRule scan(final Collection<Class<?>> classes) {
return RewriteRule.chain(classes.stream().map(RuleScanner::scan).toList());
}

public static RewriteRule scan(final Class<?> clazz) {
final List<RewriteRule> rules = new ArrayList<>();
methods: for (final Method method : clazz.getDeclaredMethods()) {
if (isNotValidMethod(method)) {
continue;
}
// only public static methods
for (final Map.Entry<Class<? extends Annotation>, AnnotatedMethodFactory<?, ?>> entry : FACTORIES.entrySet()) {
final Class<? extends Annotation> annotation = entry.getKey();
final AnnotatedMethodFactory<?, ?> factory = entry.getValue();
if (method.isAnnotationPresent(annotation)) {
rules.add(buildRule(method, annotation, factory));
continue methods;
}
}
}

return RewriteRule.chain(rules);
}

public static boolean isNotValidMethod(final Method method) {
if (method.isBridge() || method.isSynthetic()) {
return true;
}
return !Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers());
}

@SuppressWarnings("unchecked")
@ApiStatus.Internal
public static <A extends Annotation> RewriteRule buildRule(final Method method, final Class<A> annotationClass, final AnnotatedMethodFactory<?, ?> factory) {
final A instance = Objects.requireNonNull(method.getAnnotation(annotationClass));
return ((AnnotatedMethodFactory<A, ?>) factory).create(method, instance);
}

public interface AnnotatedMethodFactory<A extends Annotation, R extends RewriteRule> {

R create(Method method, A annotation);
}

private RuleScanner() {
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,63 @@
package io.papermc.asm.rules.builder.matcher.method;

import io.papermc.asm.rules.builder.matcher.method.targeted.MethodMatcherPredicate;
import java.lang.constant.MethodTypeDesc;
import java.util.Collection;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import org.jetbrains.annotations.Unmodifiable;

@FunctionalInterface
public interface MethodMatcher {
import static io.papermc.asm.util.DescriptorUtils.methodDesc;

public interface MethodMatcher extends MethodMatcherPredicate {

static MethodMatcherBuilder builder() {
return new MethodMatcherBuilderImpl();
}

static MethodMatcher falseMatcher() {
return MethodMatcherImpl.FALSE;
}

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

boolean matches(int opcode, boolean isInvokeDynamic, String name, String descriptor);
static MethodMatcher create(final Collection<String> methodNames, final Predicate<? super MethodTypeDesc> bytecodeDescPredicate) {
return new MethodMatcherImpl(methodNames, bytecodeDescPredicate, (opcode, isInvokeDynamic) -> true);
}

static MethodMatcher create(final Collection<String> methodNames, final Predicate<? super MethodTypeDesc> bytecodeDescPredicate, final OpcodePredicate opcodePredicate) {
return new MethodMatcherImpl(methodNames, bytecodeDescPredicate, opcodePredicate);
}

default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) {
return this.matches(opcode, isInvokeDynamic, name, descriptor.descriptorString());
default boolean matches(final String name, final String descriptor) {
return this.matches(name, methodDesc(descriptor));
}

boolean matches(String name, MethodTypeDesc descriptor);

@Override
default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final String descriptor) {
return this.matches(opcode, isInvokeDynamic, name, methodDesc(descriptor));
}

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

@Unmodifiable Set<String> methodNames();

Predicate<? super MethodTypeDesc> bytecodeDescPredicate();

OpcodePredicate opcodePredicate();

/**
* Creates a method matcher that matches if either matcher passes.
*
* @param other the other matcher
* @return a new "or" matcher
*/
default MethodMatcher or(final MethodMatcher other) {
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) || other.matches(opcode, isInvokeDynamic, name, descriptor);
}
MethodMatcher or(final MethodMatcher other);

/**
* Creates a method matcher that matches if both matcher pass.
Expand All @@ -38,9 +66,7 @@ default MethodMatcher or(final MethodMatcher other) {
* @return a new "and" matcher
* @see #and(Predicate)
*/
default MethodMatcher and(final MethodMatcher other) {
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && other.matches(opcode, isInvokeDynamic, name, descriptor);
}
MethodMatcher and(final MethodMatcher other);

/**
* Creates a method matcher tha matches if both this matcher
Expand All @@ -49,16 +75,5 @@ default MethodMatcher and(final MethodMatcher other) {
* @param descPredicate the method descriptor predicate
* @return a new "and" matcher
*/
default MethodMatcher and(final Predicate<? super MethodTypeDesc> descPredicate) {
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && descPredicate.test(MethodTypeDesc.ofDescriptor(descriptor));
}

/**
* Creates a method matcher that is the inverse of this matcher.
*
* @return the inverse matcher
*/
default MethodMatcher negate() {
return (opcode, isInvokeDynamic, name, descriptor) -> !this.matches(opcode, isInvokeDynamic, name, descriptor);
}
MethodMatcher and(final Predicate<? super MethodTypeDesc> descPredicate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;

class MethodMatcherBuilderImpl implements MethodMatcherBuilder {
private MethodMatcher matcher = (o, isInvokeDynamic, n, d) -> false;
private MethodMatcher matcher = MethodMatcher.falseMatcher();

MethodMatcherBuilderImpl() {
}
Expand All @@ -34,8 +33,7 @@ public MethodMatcherBuilder match(final String name, final Consumer<MatchBuilder

@Override
public MethodMatcherBuilder match(final Collection<String> names, final Consumer<MatchBuilder> matchBuilderConsumer) {
final Collection<String> namesCopy = Set.copyOf(names);
final SpecificMatchBuilder matchBuilder = new SpecificMatchBuilder(namesCopy::contains);
final SpecificMatchBuilder matchBuilder = new SpecificMatchBuilder(names);
matchBuilderConsumer.accept(matchBuilder);
matchBuilder.apply();
return this;
Expand All @@ -49,18 +47,20 @@ public MethodMatcherBuilder desc(final Predicate<? super MethodTypeDesc> descPre

final class SpecificMatchBuilder implements MatchBuilder {

private final Predicate<String> namePredicate;
private final Collection<String> methodNames;
private Predicate<? super MethodTypeDesc> bytecodeDescPredicate = $ -> true;
private BiPredicate<Integer, Boolean> opcodePredicate = ($, $$) -> true;
private OpcodePredicate opcodePredicate = ($, $$) -> true;

private SpecificMatchBuilder(final Predicate<String> namePredicate) {
this.namePredicate = namePredicate;
private SpecificMatchBuilder(final Collection<String> methodNames) {
this.methodNames = Set.copyOf(methodNames);
}

private void apply() {
MethodMatcherBuilderImpl.this.matcher = MethodMatcherBuilderImpl.this.matcher.or((o, isInvokeDynamic, n, d) -> {
return this.namePredicate.test(n) && this.opcodePredicate.test(o, isInvokeDynamic) && this.bytecodeDescPredicate.test(MethodTypeDesc.ofDescriptor(d));
});
MethodMatcherBuilderImpl.this.matcher = MethodMatcherBuilderImpl.this.matcher.or(MethodMatcher.create(
this.methodNames,
this.bytecodeDescPredicate,
this.opcodePredicate
));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package io.papermc.asm.rules.builder.matcher.method;

import java.lang.constant.MethodTypeDesc;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import org.jetbrains.annotations.Unmodifiable;

class MethodMatcherImpl implements MethodMatcher {

static final MethodMatcher FALSE = new MethodMatcherImpl(Collections.emptySet(), $ -> false, ($, $$) -> false);

private final Set<String> methodNames;
private final Predicate<? super MethodTypeDesc> descriptorPredicate;
private final OpcodePredicate opcodePredicate;

MethodMatcherImpl(final Collection<String> methodNames, final Predicate<? super MethodTypeDesc> bytecodeDescPredicate, final OpcodePredicate opcodePredicate) {
this.methodNames = Set.copyOf(methodNames);
this.descriptorPredicate = bytecodeDescPredicate;
this.opcodePredicate = opcodePredicate;
}

@Override
public boolean matches(final String name, final MethodTypeDesc descriptor) {
return this.methodNames.contains(name) && this.descriptorPredicate.test(descriptor);
}

@Override
public boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) {
return this.matches(name, descriptor) && this.opcodePredicate.matches(opcode, isInvokeDynamic); // idk which order is faster
}

@Override
public @Unmodifiable Set<String> methodNames() {
return this.methodNames;
}

@Override
public Predicate<? super MethodTypeDesc> bytecodeDescPredicate() {
return this.descriptorPredicate;
}

@Override
public OpcodePredicate opcodePredicate() {
return this.opcodePredicate;
}

@Override
public MethodMatcher or(final MethodMatcher other) {
final Set<String> copy = new HashSet<>(this.methodNames);
copy.addAll(other.methodNames());
return new MethodMatcherImpl(
copy,
(d) -> this.descriptorPredicate.test( d) || other.bytecodeDescPredicate().test(d),
(o, isInvokeDynamic) -> this.opcodePredicate.matches(o, isInvokeDynamic) || other.opcodePredicate().matches(o, isInvokeDynamic)
);
}

@Override
public MethodMatcher and(final MethodMatcher other) {
final Set<String> copy = new HashSet<>(this.methodNames);
copy.retainAll(other.methodNames());
return new MethodMatcherImpl(
copy,
(d) -> this.descriptorPredicate.test(d) && other.bytecodeDescPredicate().test(d),
(o, isInvokeDynamic) -> this.opcodePredicate.matches(o, isInvokeDynamic) && other.opcodePredicate().matches(o, isInvokeDynamic)
);
}

@Override
public MethodMatcher and(final Predicate<? super MethodTypeDesc> descPredicate) {
return new MethodMatcherImpl(
this.methodNames,
(d) -> this.descriptorPredicate.test(d) && descPredicate.test(d),
this.opcodePredicate
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ default B hasParam(final ClassDesc paramClassDesc, final int paramIdx) {
return this.desc(d -> d.parameterType(paramIdx).equals(paramClassDesc));
}

default B doesntHaveParam(final ClassDesc paramClassDesc) {
return this.desc(d -> !d.parameterList().contains(paramClassDesc));
}

default B hasReturn(final ClassDesc returnClassDesc) {
return this.desc(d -> d.returnType().equals(returnClassDesc));
}
Expand Down
Loading