Skip to content

Commit d183b88

Browse files
committed
Match AT constant
Fix NPE in SplitMethodCancellationHelper
1 parent f7534f2 commit d183b88

7 files changed

Lines changed: 115 additions & 31 deletions

File tree

core/src/main/java/org/sinytra/adapter/env/ann/AtData.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@
1414
import org.sinytra.adapter.util.MethodQualifier;
1515
import org.spongepowered.asm.mixin.injection.At;
1616

17+
import java.util.List;
1718
import java.util.Objects;
1819
import java.util.Optional;
1920

2021
public class AtData {
2122
public static final PropertyContainerTemplate TEMPLATE = PropertyContainerTemplate.builder()
2223
.require(Keys.VALUE)
23-
.keys(Keys.TARGET, Keys.ORDINAL, Keys.SHIFT, Keys.BY)
24+
.keys(Keys.TARGET, Keys.ORDINAL, Keys.SHIFT, Keys.BY, Keys.ARGS)
2425
.build();
2526

2627
private final PropertyContainer properties;
@@ -156,6 +157,7 @@ public AtData build() {
156157
}
157158
}
158159

160+
@SuppressWarnings({"unchecked", "rawtypes"})
159161
public static class Keys {
160162
public static final PropertyKey<String> VALUE = PropertyKey.create("value", String.class);
161163
public static final PropertyKey<String> TARGET = PropertyKey.<String>builder("target")
@@ -165,5 +167,6 @@ public static class Keys {
165167
public static final PropertyKey<Integer> ORDINAL = PropertyKey.create("ordinal", Integer.class);
166168
public static final PropertyKey<At.Shift> SHIFT = PropertyKey.create("shift", At.Shift.class);
167169
public static final PropertyKey<Integer> BY = PropertyKey.create("by", Integer.class);
170+
public static final PropertyKey<List<String>> ARGS = (PropertyKey) PropertyKey.create("args", List.class);
168171
}
169172
}

core/src/main/java/org/sinytra/adapter/env/util/MixinAnnotationConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class MixinAnnotationConstants {
99
public static final String AT_VAL_INVOKE_ASSIGN = "INVOKE_ASSIGN";
1010
public static final String AT_VAL_RETURN = "RETURN";
1111
public static final String AT_VAL_STORE = "STORE";
12+
public static final String AT_VAL_CONST = "CONSTANT";
1213
public static final String AT_VAL_SINYTRA_INSTANCEOF = "sinytra:INSTANCEOF";
1314
public static final String AT_SHIFT = "shift";
1415

core/src/main/java/org/sinytra/adapter/patch/mixin/ModifyExpressionValueMixin.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,15 @@ public TxResult preProcess(MixinContext context, MutableConfiguration clean, Res
5454

5555
@Override
5656
public TxResult postProcess(MixinContext context, Configuration clean, MutableConfiguration dirty, Recipe recipe) {
57-
if (dirty.getTargetMethod() == null || dirty.getAtData() == null || !dirty.getAtData().getValue().equals(AT_VAL_INVOKE)) {
57+
if (dirty.getTargetMethod() == null || dirty.getAtData() == null) {
5858
return TxResult.FAIL;
5959
}
60+
// Best effort for non-INVOKE
61+
if (!dirty.getAtData().getValue().equals(AT_VAL_INVOKE)) {
62+
dirty.inheritParameters();
63+
dirty.inheritReturnType();
64+
return TxResult.SUCCESS;
65+
}
6066

6167
MethodQualifier targetDesc = dirty.getAtData().getTarget().flatMap(MethodQualifier::parse).orElse(null);
6268
if (targetDesc == null) {

core/src/main/java/org/sinytra/adapter/patch/resolver/injection/InjectionPointResolver.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package org.sinytra.adapter.patch.resolver.injection;
22

33
import org.jetbrains.annotations.Nullable;
4+
import org.objectweb.asm.Type;
45
import org.objectweb.asm.tree.AbstractInsnNode;
56
import org.sinytra.adapter.env.ctx.MixinContext;
67
import org.sinytra.adapter.patch.Recipe;
78
import org.sinytra.adapter.patch.config.Configuration;
89
import org.sinytra.adapter.patch.resolver.CompoundResolver;
910
import org.sinytra.adapter.env.ctx.TargetPair;
11+
import org.sinytra.adapter.util.MethodQualifier;
1012

1113
import java.util.List;
1214

@@ -26,13 +28,21 @@ protected boolean canApply(Recipe recipe) {
2628
@Nullable
2729
@Override
2830
protected Configuration tryReuse(MixinContext context, Recipe recipe) {
29-
TargetPair dirtyTarget = recipe.getDirtyTarget();
31+
Configuration dirty = recipe.dirty();
32+
33+
String targetClass = dirty.getTargetClass();
34+
if (targetClass == null) return null;
35+
36+
MethodQualifier targetQualifier = dirty.getTargetMethod();
37+
if (targetQualifier == null) return null;
38+
39+
TargetPair dirtyTarget = context.methods().findMethodPair(context.dirtyLookup(), targetQualifier.withOwner(Type.getObjectType(targetClass)));
3040
if (dirtyTarget == null) return null;
3141

3242
// Try reusing the original
3343
List<AbstractInsnNode> insns = context.methods().findInjectionTargetInsns(dirtyTarget);
3444
if (!insns.isEmpty()) {
35-
return recipe.dirty().copyClean()
45+
return dirty.copyClean()
3646
.inheritAtData();
3747
}
3848

core/src/main/java/org/sinytra/adapter/patch/resolver/target/SplitMethodCancellationHelper.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ public final class SplitMethodCancellationHelper {
2323

2424
public static void handle(MixinContext context, Recipe recipe, MethodNode newTarget) {
2525
TargetPair cleanTarget = recipe.getCleanTarget();
26-
TargetPair originalTarget = recipe.getDirtyTarget();
26+
if (cleanTarget == null) return;
27+
TargetPair newCleanTarget = recipe.getNewCleanTarget();
28+
if (newCleanTarget == null) return;
2729

28-
ClassNode originalClassTarget = originalTarget.classNode();
29-
MethodNode originalMethodTarget = originalTarget.methodNode();
30+
ClassNode originalClassTarget = newCleanTarget.classNode();
31+
MethodNode originalMethodTarget = newCleanTarget.methodNode();
3032

3133
if (!MethodAnalyzer.isDirtyDeprecatedMethod(cleanTarget.methodNode(), originalMethodTarget) || Type.getReturnType(originalMethodTarget.desc) != Type.VOID_TYPE) {
3234
return;
@@ -35,7 +37,7 @@ public static void handle(MixinContext context, Recipe recipe, MethodNode newTar
3537
MixinClassGenerator generator = context.patchContext().environment().classGenerator();
3638
ClassNode generatedTarget = generator.getOrGenerateMixinClass(context.classNode(), originalClassTarget.name, null);
3739

38-
List<MethodNode> invocations = MethodAnalyzer.getOwnMethodCalls(originalTarget);
40+
List<MethodNode> invocations = MethodAnalyzer.getOwnMethodCalls(newCleanTarget);
3941
if (invocations == null) {
4042
return;
4143
}
Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,97 @@
11
package org.sinytra.adapter.transform.patch;
22

3-
import com.google.common.collect.ImmutableMap;
3+
import com.google.common.collect.ImmutableList;
44
import org.sinytra.adapter.patch.config.PropertyContainer;
55
import org.sinytra.adapter.patch.config.PropertyKey;
66

7-
import java.util.HashMap;
8-
import java.util.Map;
7+
import java.util.*;
8+
import java.util.function.Consumer;
99
import java.util.function.Predicate;
1010

1111
public class ConfigurationMatcher {
12-
private final Map<PropertyKey<?>, Predicate<?>> matchers;
12+
private final List<SubMatcher> matchers;
1313

14-
private ConfigurationMatcher(Map<PropertyKey<?>, Predicate<?>> matchers) {
15-
this.matchers = ImmutableMap.copyOf(matchers);
14+
private ConfigurationMatcher(List<SubMatcher> matchers) {
15+
this.matchers = ImmutableList.copyOf(matchers);
1616
}
1717

18-
@SuppressWarnings({"rawtypes", "unchecked"})
1918
public boolean match(PropertyContainer container) {
20-
for (Map.Entry<PropertyKey<?>, Predicate<?>> entry : this.matchers.entrySet()) {
21-
Object value = container.getProperty(entry.getKey()).orElse(null);
22-
if (value == null || !((Predicate) entry.getValue()).test(value)) {
19+
for (SubMatcher subMatcher : this.matchers) {
20+
if (!subMatcher.match(container)) {
2321
return false;
2422
}
2523
}
2624
return true;
2725
}
2826

27+
public interface SubMatcher {
28+
boolean match(PropertyContainer container);
29+
}
30+
31+
private record SingleMatcher<T>(PropertyKey<T> key, Predicate<T> predicate) implements SubMatcher {
32+
private SingleMatcher(PropertyKey<T> key, Predicate<T> predicate) {
33+
this.key = Objects.requireNonNull(key);
34+
this.predicate = Objects.requireNonNull(predicate);
35+
}
36+
37+
@Override
38+
public boolean match(PropertyContainer container) {
39+
T value = container.getProperty(this.key).orElse(null);
40+
return value != null && this.predicate.test(value);
41+
}
42+
}
43+
44+
private record OrMatcher(List<SubMatcher> matchers) implements SubMatcher {
45+
@Override
46+
public boolean match(PropertyContainer container) {
47+
for (SubMatcher matcher : this.matchers) {
48+
if (matcher.match(container)) {
49+
return true;
50+
}
51+
}
52+
return false;
53+
}
54+
}
55+
2956
public static Builder builder() {
3057
return new Builder();
3158
}
3259

3360
public static class Builder {
34-
private final Map<PropertyKey<?>, Predicate<?>> matchers = new HashMap<>();
61+
private final Map<PropertyKey<?>, Predicate<?>> singleMatchers = new HashMap<>();
62+
private final List<SubMatcher> matchers = new ArrayList<>();
3563

3664
@SuppressWarnings({"rawtypes", "unchecked"})
3765
public <T> Builder match(PropertyKey<T> prop, Predicate<T> predicate) {
38-
this.matchers.compute(prop, (k, v) -> v == null ? predicate : v.or((Predicate) predicate));
66+
this.singleMatchers.compute(prop, (k, v) -> v == null ? predicate : v.or((Predicate) predicate));
67+
return this;
68+
}
69+
70+
public Builder match(SubMatcher subMatcher) {
71+
this.matchers.add(subMatcher);
72+
return this;
73+
}
74+
75+
public Builder or(Consumer<Builder> subBuilder) {
76+
Builder sub = new Builder();
77+
subBuilder.accept(sub);
78+
match(new OrMatcher(sub.buildMatchers()));
3979
return this;
4080
}
4181

82+
@SuppressWarnings({"rawtypes", "unchecked"})
83+
public List<SubMatcher> buildMatchers() {
84+
List<SubMatcher> all = new ArrayList<>();
85+
this.singleMatchers.forEach((key, pred) -> {
86+
SingleMatcher matcher = new SingleMatcher(key, pred);
87+
all.addFirst(matcher);
88+
});
89+
all.addAll(this.matchers);
90+
return all;
91+
}
92+
4293
public ConfigurationMatcher build() {
43-
return new ConfigurationMatcher(this.matchers);
94+
return new ConfigurationMatcher(buildMatchers());
4495
}
4596
}
4697
}

core/src/main/java/org/sinytra/adapter/transform/patch/MethodPatchBuilderImpl.java

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import org.objectweb.asm.commons.InstructionAdapter;
55
import org.sinytra.adapter.env.ann.AtData;
66
import org.sinytra.adapter.env.param.MethodParameters;
7+
import org.sinytra.adapter.env.util.MixinAnnotationConstants;
78
import org.sinytra.adapter.patch.config.Configuration;
8-
import org.sinytra.adapter.patch.config.key.MixinKeys;
99
import org.sinytra.adapter.patch.config.MutableConfiguration;
10+
import org.sinytra.adapter.patch.config.key.MixinKeys;
1011
import org.sinytra.adapter.patch.config.key.SpecialKeys;
1112
import org.sinytra.adapter.transform.MethodTransformer;
1213
import org.sinytra.adapter.util.MethodQualifier;
@@ -21,12 +22,14 @@
2122
import java.util.stream.Stream;
2223

2324
import static java.util.function.Predicate.isEqual;
24-
import static org.sinytra.adapter.patch.config.key.ControlKeys.*;
25+
import static org.sinytra.adapter.patch.config.key.ControlKeys.MIXIN_TYPE;
26+
import static org.sinytra.adapter.patch.config.key.ControlKeys.TARGET_CLASS;
2527

2628
public class MethodPatchBuilderImpl implements MethodPatchBuilder {
2729
private final ConfigurationMatcher.Builder matcher = ConfigurationMatcher.builder();
2830
private final MutableConfiguration config = MutableConfiguration.create();
29-
private BiConsumer<Configuration, MutableConfiguration> configCompleter = (a, b) -> {};
31+
private BiConsumer<Configuration, MutableConfiguration> configCompleter = (a, b) -> {
32+
};
3033
private final List<MethodTransformer> transforms = new ArrayList<>();
3134

3235
@Override
@@ -71,10 +74,18 @@ public MethodPatchBuilder targetInjectionPoint(String value, @Nullable String ta
7174

7275
@Override
7376
public MethodPatchBuilder targetConstant(double doubleValue) {
74-
this.matcher.match(MixinKeys.TARGET_CONSTANT, c -> {
75-
// TODO OR check at const value
76-
Optional<Double> opt = c.doubleValue();
77-
return opt.isPresent() && opt.get() == doubleValue;
77+
this.matcher.or(sub -> {
78+
sub.match(MixinKeys.TARGET_CONSTANT, c -> {
79+
Optional<Double> opt = c.doubleValue();
80+
return opt.isPresent() && opt.get() == doubleValue;
81+
});
82+
sub.match(MixinKeys.TARGET_AT, at -> {
83+
if (!at.getValue().equals(MixinAnnotationConstants.AT_VAL_CONST)) {
84+
return false;
85+
}
86+
List<String> args = at.getProperty(AtData.Keys.ARGS).orElseGet(List::of);
87+
return args.contains("doubleValue=" + doubleValue + "D");
88+
});
7889
});
7990
return this;
8091
}
@@ -123,9 +134,9 @@ public MethodPatchBuilder replaceInjectionPoint(String value, String target) {
123134
@Override
124135
public MethodPatchBuilder modifyParams(UnaryOperator<MethodParameters> op) {
125136
this.configCompleter = this.configCompleter.andThen((clean, dirty) -> {
126-
MethodParameters params = clean.getParameters().copy();
127-
MethodParameters newParams = op.apply(params);
128-
dirty.setParameters(newParams);
137+
MethodParameters params = clean.getParameters().copy();
138+
MethodParameters newParams = op.apply(params);
139+
dirty.setParameters(newParams);
129140
});
130141
return this;
131142
}

0 commit comments

Comments
 (0)