Skip to content

Commit dbe49a4

Browse files
committed
Add more asm utils api for future use in Commodore
1 parent 8957227 commit dbe49a4

35 files changed

+747
-103
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package io.papermc.asm.rules;
2+
3+
import io.papermc.asm.rules.method.DirectStaticRewrite;
4+
import java.lang.annotation.Annotation;
5+
import java.lang.reflect.Method;
6+
import java.lang.reflect.Modifier;
7+
import java.util.ArrayList;
8+
import java.util.Collection;
9+
import java.util.Collections;
10+
import java.util.HashMap;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.Objects;
14+
import org.jetbrains.annotations.ApiStatus;
15+
16+
public final class RuleScanner {
17+
18+
private static final Map<Class<? extends Annotation>, AnnotatedMethodFactory<?, ?>> INTERNAL_FACTORIES = new HashMap<>();
19+
20+
static {
21+
register(DirectStaticRewrite.Wrapper.class, DirectStaticRewrite::create);
22+
}
23+
24+
public static final Map<Class<? extends Annotation>, AnnotatedMethodFactory<?, ?>> FACTORIES = Collections.unmodifiableMap(INTERNAL_FACTORIES);
25+
26+
private static <A extends Annotation> void register(final Class<A> annotationClass, final AnnotatedMethodFactory<A, ? extends RewriteRule> factory) {
27+
if (INTERNAL_FACTORIES.containsKey(annotationClass)) {
28+
throw new IllegalArgumentException("Factory for " + annotationClass.getName() + " is already registered");
29+
}
30+
INTERNAL_FACTORIES.put(annotationClass, factory);
31+
}
32+
33+
public static RewriteRule scan(final Collection<Class<?>> classes) {
34+
return RewriteRule.chain(classes.stream().map(RuleScanner::scan).toList());
35+
}
36+
37+
public static RewriteRule scan(final Class<?> clazz) {
38+
final List<RewriteRule> rules = new ArrayList<>();
39+
methods: for (final Method method : clazz.getDeclaredMethods()) {
40+
if (isNotValidMethod(method)) {
41+
continue;
42+
}
43+
// only public static methods
44+
for (final Map.Entry<Class<? extends Annotation>, AnnotatedMethodFactory<?, ?>> entry : FACTORIES.entrySet()) {
45+
final Class<? extends Annotation> annotation = entry.getKey();
46+
final AnnotatedMethodFactory<?, ?> factory = entry.getValue();
47+
if (method.isAnnotationPresent(annotation)) {
48+
rules.add(buildRule(method, annotation, factory));
49+
continue methods;
50+
}
51+
}
52+
}
53+
54+
return RewriteRule.chain(rules);
55+
}
56+
57+
public static boolean isNotValidMethod(final Method method) {
58+
if (method.isBridge() || method.isSynthetic()) {
59+
return true;
60+
}
61+
return !Modifier.isStatic(method.getModifiers()) || !Modifier.isPublic(method.getModifiers());
62+
}
63+
64+
@SuppressWarnings("unchecked")
65+
@ApiStatus.Internal
66+
public static <A extends Annotation> RewriteRule buildRule(final Method method, final Class<A> annotationClass, final AnnotatedMethodFactory<?, ?> factory) {
67+
final A instance = Objects.requireNonNull(method.getAnnotation(annotationClass));
68+
return ((AnnotatedMethodFactory<A, ?>) factory).create(method, instance);
69+
}
70+
71+
public interface AnnotatedMethodFactory<A extends Annotation, R extends RewriteRule> {
72+
73+
R create(Method method, A annotation);
74+
}
75+
76+
private RuleScanner() {
77+
}
78+
}
Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,63 @@
11
package io.papermc.asm.rules.builder.matcher.method;
22

3+
import io.papermc.asm.rules.builder.matcher.method.targeted.MethodMatcherPredicate;
34
import java.lang.constant.MethodTypeDesc;
5+
import java.util.Collection;
6+
import java.util.Set;
47
import java.util.function.Consumer;
58
import java.util.function.Predicate;
9+
import org.jetbrains.annotations.Unmodifiable;
610

7-
@FunctionalInterface
8-
public interface MethodMatcher {
11+
import static io.papermc.asm.util.DescriptorUtils.methodDesc;
12+
13+
public interface MethodMatcher extends MethodMatcherPredicate {
914

1015
static MethodMatcherBuilder builder() {
1116
return new MethodMatcherBuilderImpl();
1217
}
1318

19+
static MethodMatcher falseMatcher() {
20+
return MethodMatcherImpl.FALSE;
21+
}
22+
1423
static MethodMatcher single(final String name, final Consumer<MethodMatcherBuilder.MatchBuilder> matchBuilderConsumer) {
1524
return builder().match(name, matchBuilderConsumer).build();
1625
}
1726

18-
boolean matches(int opcode, boolean isInvokeDynamic, String name, String descriptor);
27+
static MethodMatcher create(final Collection<String> methodNames, final Predicate<? super MethodTypeDesc> bytecodeDescPredicate) {
28+
return new MethodMatcherImpl(methodNames, bytecodeDescPredicate, (opcode, isInvokeDynamic) -> true);
29+
}
30+
31+
static MethodMatcher create(final Collection<String> methodNames, final Predicate<? super MethodTypeDesc> bytecodeDescPredicate, final OpcodePredicate opcodePredicate) {
32+
return new MethodMatcherImpl(methodNames, bytecodeDescPredicate, opcodePredicate);
33+
}
1934

20-
default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) {
21-
return this.matches(opcode, isInvokeDynamic, name, descriptor.descriptorString());
35+
default boolean matches(final String name, final String descriptor) {
36+
return this.matches(name, methodDesc(descriptor));
2237
}
2338

39+
boolean matches(String name, MethodTypeDesc descriptor);
40+
41+
@Override
42+
default boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final String descriptor) {
43+
return this.matches(opcode, isInvokeDynamic, name, methodDesc(descriptor));
44+
}
45+
46+
boolean matches(int opcode, boolean isInvokeDynamic, String name, MethodTypeDesc descriptor);
47+
48+
@Unmodifiable Set<String> methodNames();
49+
50+
Predicate<? super MethodTypeDesc> bytecodeDescPredicate();
51+
52+
OpcodePredicate opcodePredicate();
53+
2454
/**
2555
* Creates a method matcher that matches if either matcher passes.
2656
*
2757
* @param other the other matcher
2858
* @return a new "or" matcher
2959
*/
30-
default MethodMatcher or(final MethodMatcher other) {
31-
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) || other.matches(opcode, isInvokeDynamic, name, descriptor);
32-
}
60+
MethodMatcher or(final MethodMatcher other);
3361

3462
/**
3563
* Creates a method matcher that matches if both matcher pass.
@@ -38,9 +66,7 @@ default MethodMatcher or(final MethodMatcher other) {
3866
* @return a new "and" matcher
3967
* @see #and(Predicate)
4068
*/
41-
default MethodMatcher and(final MethodMatcher other) {
42-
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && other.matches(opcode, isInvokeDynamic, name, descriptor);
43-
}
69+
MethodMatcher and(final MethodMatcher other);
4470

4571
/**
4672
* Creates a method matcher tha matches if both this matcher
@@ -49,16 +75,5 @@ default MethodMatcher and(final MethodMatcher other) {
4975
* @param descPredicate the method descriptor predicate
5076
* @return a new "and" matcher
5177
*/
52-
default MethodMatcher and(final Predicate<? super MethodTypeDesc> descPredicate) {
53-
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && descPredicate.test(MethodTypeDesc.ofDescriptor(descriptor));
54-
}
55-
56-
/**
57-
* Creates a method matcher that is the inverse of this matcher.
58-
*
59-
* @return the inverse matcher
60-
*/
61-
default MethodMatcher negate() {
62-
return (opcode, isInvokeDynamic, name, descriptor) -> !this.matches(opcode, isInvokeDynamic, name, descriptor);
63-
}
78+
MethodMatcher and(final Predicate<? super MethodTypeDesc> descPredicate);
6479
}

src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodMatcherBuilderImpl.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,11 @@
66
import java.util.Collection;
77
import java.util.Collections;
88
import java.util.Set;
9-
import java.util.function.BiPredicate;
109
import java.util.function.Consumer;
1110
import java.util.function.Predicate;
1211

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

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

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

5048
final class SpecificMatchBuilder implements MatchBuilder {
5149

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

56-
private SpecificMatchBuilder(final Predicate<String> namePredicate) {
57-
this.namePredicate = namePredicate;
54+
private SpecificMatchBuilder(final Collection<String> methodNames) {
55+
this.methodNames = Set.copyOf(methodNames);
5856
}
5957

6058
private void apply() {
61-
MethodMatcherBuilderImpl.this.matcher = MethodMatcherBuilderImpl.this.matcher.or((o, isInvokeDynamic, n, d) -> {
62-
return this.namePredicate.test(n) && this.opcodePredicate.test(o, isInvokeDynamic) && this.bytecodeDescPredicate.test(MethodTypeDesc.ofDescriptor(d));
63-
});
59+
MethodMatcherBuilderImpl.this.matcher = MethodMatcherBuilderImpl.this.matcher.or(MethodMatcher.create(
60+
this.methodNames,
61+
this.bytecodeDescPredicate,
62+
this.opcodePredicate
63+
));
6464
}
6565

6666
@Override
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package io.papermc.asm.rules.builder.matcher.method;
2+
3+
import java.lang.constant.MethodTypeDesc;
4+
import java.util.Collection;
5+
import java.util.Collections;
6+
import java.util.HashSet;
7+
import java.util.Set;
8+
import java.util.function.Predicate;
9+
import org.jetbrains.annotations.Unmodifiable;
10+
11+
class MethodMatcherImpl implements MethodMatcher {
12+
13+
static final MethodMatcher FALSE = new MethodMatcherImpl(Collections.emptySet(), $ -> false, ($, $$) -> false);
14+
15+
private final Set<String> methodNames;
16+
private final Predicate<? super MethodTypeDesc> descriptorPredicate;
17+
private final OpcodePredicate opcodePredicate;
18+
19+
MethodMatcherImpl(final Collection<String> methodNames, final Predicate<? super MethodTypeDesc> bytecodeDescPredicate, final OpcodePredicate opcodePredicate) {
20+
this.methodNames = Set.copyOf(methodNames);
21+
this.descriptorPredicate = bytecodeDescPredicate;
22+
this.opcodePredicate = opcodePredicate;
23+
}
24+
25+
@Override
26+
public boolean matches(final String name, final MethodTypeDesc descriptor) {
27+
return this.methodNames.contains(name) && this.descriptorPredicate.test(descriptor);
28+
}
29+
30+
@Override
31+
public boolean matches(final int opcode, final boolean isInvokeDynamic, final String name, final MethodTypeDesc descriptor) {
32+
return this.matches(name, descriptor) && this.opcodePredicate.matches(opcode, isInvokeDynamic); // idk which order is faster
33+
}
34+
35+
@Override
36+
public @Unmodifiable Set<String> methodNames() {
37+
return this.methodNames;
38+
}
39+
40+
@Override
41+
public Predicate<? super MethodTypeDesc> bytecodeDescPredicate() {
42+
return this.descriptorPredicate;
43+
}
44+
45+
@Override
46+
public OpcodePredicate opcodePredicate() {
47+
return this.opcodePredicate;
48+
}
49+
50+
@Override
51+
public MethodMatcher or(final MethodMatcher other) {
52+
final Set<String> copy = new HashSet<>(this.methodNames);
53+
copy.addAll(other.methodNames());
54+
return new MethodMatcherImpl(
55+
copy,
56+
(d) -> this.descriptorPredicate.test( d) || other.bytecodeDescPredicate().test(d),
57+
(o, isInvokeDynamic) -> this.opcodePredicate.matches(o, isInvokeDynamic) || other.opcodePredicate().matches(o, isInvokeDynamic)
58+
);
59+
}
60+
61+
@Override
62+
public MethodMatcher and(final MethodMatcher other) {
63+
final Set<String> copy = new HashSet<>(this.methodNames);
64+
copy.retainAll(other.methodNames());
65+
return new MethodMatcherImpl(
66+
copy,
67+
(d) -> this.descriptorPredicate.test(d) && other.bytecodeDescPredicate().test(d),
68+
(o, isInvokeDynamic) -> this.opcodePredicate.matches(o, isInvokeDynamic) && other.opcodePredicate().matches(o, isInvokeDynamic)
69+
);
70+
}
71+
72+
@Override
73+
public MethodMatcher and(final Predicate<? super MethodTypeDesc> descPredicate) {
74+
return new MethodMatcherImpl(
75+
this.methodNames,
76+
(d) -> this.descriptorPredicate.test(d) && descPredicate.test(d),
77+
this.opcodePredicate
78+
);
79+
}
80+
}

src/main/java/io/papermc/asm/rules/builder/matcher/method/MethodParamMatcherBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ default B hasParam(final ClassDesc paramClassDesc, final int paramIdx) {
1515
return this.desc(d -> d.parameterType(paramIdx).equals(paramClassDesc));
1616
}
1717

18+
default B doesntHaveParam(final ClassDesc paramClassDesc) {
19+
return this.desc(d -> !d.parameterList().contains(paramClassDesc));
20+
}
21+
1822
default B hasReturn(final ClassDesc returnClassDesc) {
1923
return this.desc(d -> d.returnType().equals(returnClassDesc));
2024
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.papermc.asm.rules.builder.matcher.method.targeted;
2+
3+
@FunctionalInterface
4+
public interface MethodMatcherPredicate {
5+
6+
boolean matches(int opcode, boolean isInvokeDynamic, String name, String descriptor);
7+
8+
/**
9+
* Creates a method matcher that matches if both this matcher
10+
* and the other matcher pass.
11+
*
12+
* @param other the other matcher
13+
* @return a new "and" matcher
14+
*/
15+
default MethodMatcherPredicate and(final MethodMatcherPredicate other) {
16+
return (opcode, isInvokeDynamic, name, descriptor) -> this.matches(opcode, isInvokeDynamic, name, descriptor) && other.matches(opcode, isInvokeDynamic, name, descriptor);
17+
}
18+
19+
/**
20+
* Creates a new method matcher that is inverted.
21+
*
22+
* @return a new inverted matcher
23+
*/
24+
default MethodMatcherPredicate negate() {
25+
return (opcode, isInvokeDynamic, name, descriptor) -> !this.matches(opcode, isInvokeDynamic, name, descriptor);
26+
}
27+
}

src/main/java/io/papermc/asm/rules/builder/matcher/method/targeted/TargetedMethodMatcher.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
import io.papermc.asm.rules.builder.matcher.method.MethodMatcher;
44
import java.lang.constant.ClassDesc;
55

6-
public interface TargetedMethodMatcher extends MethodMatcher {
6+
public interface TargetedMethodMatcher {
77

88
static TargetedMethodMatcherBuilder builder() {
99
return new TargetedMethodMatcherBuilderImpl();
1010
}
1111

1212
ClassDesc targetType();
13+
14+
MethodMatcher wrapped();
1315
}

0 commit comments

Comments
 (0)