Skip to content

Commit a0210c1

Browse files
committed
Prevent matching multiple labels in method comparator
1 parent ba6dadd commit a0210c1

File tree

3 files changed

+62
-6
lines changed

3 files changed

+62
-6
lines changed

definition/src/main/java/org/sinytra/adapter/patch/transformer/dynfix/DynFixMethodComparison.java

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,20 @@
2424
import java.util.stream.Stream;
2525

2626
public class DynFixMethodComparison implements DynamicFixer<DynFixMethodComparison.Data> {
27+
private static final Set<String> ACCEPTED_ANNOTATIONS = Set.of(MixinConstants.INJECT, MixinConstants.WRAP_OPERATION);
28+
2729
public record Data(AbstractInsnNode cleanInjectionInsn) {}
2830

2931
@Nullable
3032
@Override
3133
public Data prepare(MethodContext methodContext) {
32-
if (methodContext.methodAnnotation().matchesDesc(MixinConstants.INJECT) && methodContext.findDirtyInjectionTarget() != null) {
34+
if (methodContext.methodAnnotation().matchesAny(ACCEPTED_ANNOTATIONS) && methodContext.findDirtyInjectionTarget() != null) {
3335
MethodContext.TargetPair cleanInjectionTarget = methodContext.findCleanInjectionTarget();
3436
if (cleanInjectionTarget == null) {
3537
return null;
3638
}
3739
List<AbstractInsnNode> cleanInsns = methodContext.findInjectionTargetInsns(cleanInjectionTarget);
38-
if (cleanInsns.size() == 1) {
40+
if (!cleanInsns.isEmpty()) {
3941
return new Data(cleanInsns.getFirst());
4042
}
4143
}
@@ -61,14 +63,23 @@ public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodCont
6163

6264
Map<List<AbstractInsnNode>, List<AbstractInsnNode>> matchedLabels = new LinkedHashMap<>();
6365
for (List<AbstractInsnNode> cleanInsns : cleanLabelsOriginal) {
66+
List<List<AbstractInsnNode>> candidates = new ArrayList<>();
67+
6468
for (List<AbstractInsnNode> dirtyInsns : dirtyLabels) {
6569
if (InstructionMatcher.test(cleanInsns, dirtyInsns, InsnComparator.IGNORE_VAR_INDEX | InsnComparator.IGNORE_LINE_NUMBERS)) {
66-
matchedLabels.put(cleanInsns, dirtyInsns);
67-
cleanLabels.remove(cleanInsns);
68-
dirtyLabels.remove(dirtyInsns);
69-
break;
70+
candidates.add(dirtyInsns);
7071
}
7172
}
73+
// TODO Try and come up with something better
74+
// This prevents messing up the order of labels
75+
// Without this countermeasure, it might happen that a label that was deleted will match a seemingly identical label somewhere else in the method, which is wrong
76+
// We disable any duplicated until we can properly handle such cases
77+
if (candidates.size() == 1) {
78+
List<AbstractInsnNode> dirtyInsns = candidates.getFirst();
79+
matchedLabels.put(cleanInsns, dirtyInsns);
80+
cleanLabels.remove(cleanInsns);
81+
dirtyLabels.remove(dirtyInsns);
82+
}
7283
}
7384

7485
Pair<List<AbstractInsnNode>, List<AbstractInsnNode>> patchRange = findPatchHunkRange(cleanLabel, cleanLabelsOriginal, matchedLabels);
@@ -78,6 +89,11 @@ public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodCont
7889

7990
ClassNode cleanTargetClass = methodContext.findCleanInjectionTarget().classNode();
8091
List<List<AbstractInsnNode>> hunkLabels = dirtyLabelsOriginal.subList(dirtyLabelsOriginal.indexOf(patchRange.getFirst()) + 1, dirtyLabelsOriginal.indexOf(patchRange.getSecond()));
92+
93+
if (methodContext.methodAnnotation().matchesDesc(MixinConstants.WRAP_OPERATION)) {
94+
return handleTargetModification(hunkLabels, methodContext);
95+
}
96+
8197
for (List<AbstractInsnNode> insns : hunkLabels) {
8298
for (AbstractInsnNode insn : insns) {
8399
if (insn instanceof MethodInsnNode minsn && minsn.getOpcode() == Opcodes.INVOKESTATIC && !minsn.owner.equals(cleanTargetClass.name)) {
@@ -102,6 +118,21 @@ public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodCont
102118
return Patch.Result.PASS;
103119
}
104120

121+
private static Patch.Result handleTargetModification(List<List<AbstractInsnNode>> hunkLabels, MethodContext methodContext) {
122+
ClassNode dirtyTarget = methodContext.findDirtyInjectionTarget().classNode();
123+
for (List<AbstractInsnNode> insns : hunkLabels) {
124+
for (AbstractInsnNode insn : insns) {
125+
if (insn instanceof MethodInsnNode minsn && minsn.owner.equals(dirtyTarget.name)) {
126+
MethodNode method = MethodCallAnalyzer.findMethodByUniqueName(dirtyTarget, minsn.name);
127+
if (!methodContext.findInjectionTargetInsns(new MethodContext.TargetPair(dirtyTarget, method)).isEmpty()) {
128+
return BundledMethodTransform.builder().modifyTarget(minsn.name + minsn.desc).apply(methodContext);
129+
}
130+
}
131+
}
132+
}
133+
return Patch.Result.PASS;
134+
}
135+
105136
private static Patch.Result attemptExtractMixin(MethodInsnNode minsn, MethodContext methodContext) {
106137
// Looks like some code was moved into a static method outside this class
107138
// Attempt extracting mixin
@@ -193,6 +224,9 @@ private static Patch.Result createInjectionPoint(ClassNode newTargetClass, Metho
193224
// TODO This should be an automatic upgrade tbh
194225
private static void adjustInjectorOrdinalForNewMethod(MethodInsnNode minsn, MethodContext methodContext) {
195226
AnnotationValueHandle<Integer> handle = methodContext.injectionPointAnnotationOrThrow().<Integer>getValue("ordinal").orElse(null);
227+
if (handle == null) {
228+
return;
229+
}
196230
int originalOrdinal = handle.get();
197231
// Temporarily adjust ordinal to account for previous calls that have not been moved to the new class
198232
if (handle != null) {

test/src/test/java/org/sinytra/adapter/patch/test/mixin/DynamicMixinPatchTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ void testCompareModifiedMethod2() throws Exception {
164164
);
165165
}
166166

167+
@Test
168+
void testCompareModifiedMethod3() throws Exception {
169+
assertSameCode(
170+
"org/sinytra/adapter/test/mixin/AbstractMinecartMixin",
171+
"skipVelocityClamping",
172+
assertTargetMethod(),
173+
assertInjectionPoint()
174+
);
175+
}
176+
167177
@Override
168178
protected LoadResult load(String className, List<String> allowedMethods) throws Exception {
169179
final ClassNode patched = loadClass(className);

test/src/testClasses/java/org/sinytra/adapter/test/mixin/AbstractMinecartMixin.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.sinytra.adapter.test.mixin;
22

3+
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
4+
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
35
import net.minecraft.world.entity.vehicle.AbstractMinecart;
46
import net.minecraft.world.phys.Vec3;
57
import org.spongepowered.asm.mixin.Mixin;
@@ -17,4 +19,14 @@ private Vec3 modifiedMovement(Vec3 movement) {
1719
private Vec3 modifiedMovementExpected(Vec3 movement) {
1820
return movement;
1921
}
22+
23+
@WrapOperation(method = "moveAlongTrack", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;clamp(DDD)D"))
24+
private double skipVelocityClamping(double value, double min, double max, Operation<Double> original) {
25+
return value;
26+
}
27+
28+
@WrapOperation(method = "moveMinecartOnRail(Lnet/minecraft/core/BlockPos;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Mth;clamp(DDD)D"))
29+
private double skipVelocityClampingExpected(double value, double min, double max, Operation<Double> original) {
30+
return value;
31+
}
2032
}

0 commit comments

Comments
 (0)