Skip to content

Commit

Permalink
Another wrap operation case
Browse files Browse the repository at this point in the history
Su5eD committed Aug 13, 2024
1 parent 02ccc2d commit aade659
Showing 15 changed files with 331 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;

public record LayeredParamsDiffSnapshot(List<ParamModification> modifications) implements ParamsDiffSnapshot {
@@ -21,7 +22,7 @@ public interface ParamModification {

boolean satisfiesIndexLimit(int index);

ParameterTransformer asParameterTransformer();
ParameterTransformer asParameterTransformer(Set<Flags> flags);
}

public record InsertParam(int index, Type type) implements ParamModification {
@@ -36,7 +37,7 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new InjectParameterTransform(this.index, this.type);
}
}
@@ -53,8 +54,8 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
return new ReplaceParametersTransformer(this.index, this.type);
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new ReplaceParametersTransformer(this.index, this.type, flags.contains(Flags.UPGRADE_WRAP_OP));
}
}

@@ -70,7 +71,7 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new SwapParametersTransformer(this.from, this.to);
}
}
@@ -87,7 +88,7 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new MoveParametersTransformer(this.from, this.to);
}
}
@@ -104,7 +105,7 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new RemoveParameterTransformer(this.index);
}
}
@@ -121,7 +122,7 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new InlineParameterTransformer(this.target, this.adapter);
}
}
@@ -138,7 +139,7 @@ public boolean satisfiesIndexLimit(int index) {
}

@Override
public ParameterTransformer asParameterTransformer() {
public ParameterTransformer asParameterTransformer(Set<Flags> flags) {
return new SubstituteParameterTransformer(this.target, this.substitute);
}
}
@@ -196,8 +197,8 @@ public LayeredParamsDiffSnapshot offset(int offset, int limit) {
}

@Override
public MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset, boolean upgradeWrapOperation) {
List<ParameterTransformer> transformers = this.modifications.stream().map(ParamModification::asParameterTransformer).toList();
public MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset, Set<Flags> flags) {
List<ParameterTransformer> transformers = this.modifications.stream().map(paramModification -> paramModification.asParameterTransformer(flags)).toList();
return TransformParameters.builder().transform(transformers).withOffset(withOffset).targetType(type).build();
}

Original file line number Diff line number Diff line change
@@ -5,9 +5,15 @@
import org.sinytra.adapter.patch.api.MethodTransform;
import org.sinytra.adapter.patch.transformer.operation.param.ParamTransformTarget;

import java.util.EnumSet;
import java.util.List;
import java.util.Set;

public interface ParamsDiffSnapshot {
enum Flags {
UPGRADE_WRAP_OP;
}

boolean isEmpty();

List<Pair<Integer, Type>> insertions();
@@ -19,8 +25,8 @@ public interface ParamsDiffSnapshot {
ParamsDiffSnapshot offset(int offset, int limit);

default MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset) {
return asParameterTransformer(type, withOffset, true);
return asParameterTransformer(type, withOffset, EnumSet.of(Flags.UPGRADE_WRAP_OP));
}

MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset, boolean upgradeWrapOperation);
MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset, Set<Flags> flags);
}
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
@@ -88,7 +89,7 @@ public SimpleParamsDiffSnapshot offset(int offset, int limit) {
}

@Override
public MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset, boolean upgradeWrapOperation) {
public MethodTransform asParameterTransformer(ParamTransformTarget type, boolean withOffset, Set<Flags> flags) {
List<MethodTransform> list = new ArrayList<>();
SimpleParamsDiffSnapshot light = new SimpleParamsDiffSnapshot(List.of(), this.replacements, this.swaps, this.substitutes, this.removals, this.moves, this.inlines);
if (!light.isEmpty()) {
@@ -97,7 +98,7 @@ public MethodTransform asParameterTransformer(ParamTransformTarget type, boolean
if (!this.insertions.isEmpty()) {
list.add(TransformParameters.builder()
.transform(this.insertions.stream()
.<ParameterTransformer>map(p -> new InjectParameterTransform(p.getFirst(), p.getSecond(), upgradeWrapOperation))
.<ParameterTransformer>map(p -> new InjectParameterTransform(p.getFirst(), p.getSecond(), flags.contains(Flags.UPGRADE_WRAP_OP)))
.toList())
.withOffset(withOffset)
.targetType(type)
Original file line number Diff line number Diff line change
@@ -24,6 +24,7 @@

import java.util.Comparator;
import java.util.List;
import java.util.Set;

public final class MethodUpgrader {

@@ -75,7 +76,7 @@ private static void upgradeModifyExpValue(MethodNode methodNode, MethodContext m
// Create diff
SimpleParamsDiffSnapshot diff = EnhancedParamsDiff.create(originalDesc, modifiedDesc);
if (!diff.isEmpty()) {
MethodTransform patch = diff.asParameterTransformer(ParamTransformTarget.ALL, false, false);
MethodTransform patch = diff.asParameterTransformer(ParamTransformTarget.ALL, false, Set.of());
patch.apply(methodContext);
}
}
@@ -130,7 +131,7 @@ public static void upgradeCapturedLocals(MethodNode methodNode, MethodContext me
LayeredParamsDiffSnapshot diff = EnhancedParamsDiff.createLayered(expected, required);
if (!diff.isEmpty()) {
List<ParameterTransformer> transformers = diff.modifications().stream()
.map(LayeredParamsDiffSnapshot.ParamModification::asParameterTransformer)
.map(paramModification -> paramModification.asParameterTransformer(Set.of()))
.toList();
MethodTransform patch = TransformParameters.builder().transform(transformers).withOffset().targetType(ParamTransformTarget.METHOD).build();
patch.apply(methodContext);
@@ -155,7 +156,31 @@ private static void upgradeWrapOperation(MethodNode methodNode, MethodContext me
// Create diff
SimpleParamsDiffSnapshot diff = EnhancedParamsDiff.create(originalDesc, modifiedDesc);
if (!diff.isEmpty()) {
MethodTransform patch = diff.asParameterTransformer(ParamTransformTarget.ALL, false, false);
MethodTransform patch = diff.asParameterTransformer(ParamTransformTarget.ALL, false, Set.of());
patch.apply(methodContext);
}
}

public static void upgradeWrapOperationLayered(MethodContext methodContext, MethodQualifier cleanQualifier, MethodQualifier dirtyQualifier) {
if (dirtyQualifier.owner() == null || cleanQualifier.desc() == null) {
return;
}
List<Type> originalTargetDesc = List.of(Type.getArgumentTypes(cleanQualifier.desc()));
List<Type> modifiedTargetDesc = List.of(Type.getArgumentTypes(dirtyQualifier.desc()));
MethodNode methodNode = methodContext.getMixinMethod();
List<Type> originalDesc = List.of(Type.getArgumentTypes(methodNode.desc));
List<Type> modifiedDesc = ImmutableList.<Type>builder()
// Add instance parameter
.add(Type.getType(dirtyQualifier.owner()))
// Add target parameters
.addAll(modifiedTargetDesc)
// Add everything after the original owner and target args (such as captured locals)
.addAll(originalDesc.subList(1 + originalTargetDesc.size(), originalDesc.size()))
.build();
// Create diff
LayeredParamsDiffSnapshot diff = EnhancedParamsDiff.createLayered(originalDesc, modifiedDesc);
if (!diff.isEmpty()) {
MethodTransform patch = diff.asParameterTransformer(ParamTransformTarget.ALL, false, Set.of());
patch.apply(methodContext);
}
}
Original file line number Diff line number Diff line change
@@ -4,6 +4,8 @@
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;

import java.util.function.BiConsumer;

public record SimpleTypeAdapter(Type from, Type to, TypePatch adapter) implements TypeAdapter {
public interface TypePatch {
void apply(InsnList list, AbstractInsnNode target);
@@ -13,4 +15,12 @@ public interface TypePatch {
public void apply(InsnList list, AbstractInsnNode target) {
this.adapter.apply(list, target);
}

@Override
public TypeAdapter andThen(BiConsumer<InsnList, AbstractInsnNode> consumer) {
return new SimpleTypeAdapter(this.from, this.to, (l, t) -> {
consumer.accept(l, t);
this.adapter.apply(l, t);
});
}
}
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;

import java.util.function.BiConsumer;
import java.util.function.Supplier;

public class SupplierTypeAdapter implements TypeAdapterProvider {
@@ -27,5 +28,10 @@ public void apply(InsnList list, AbstractInsnNode target) {
patch.add(new TypeInsnNode(Opcodes.CHECKCAST, this.to.getInternalName()));
list.insert(target, patch);
}

@Override
public TypeAdapter andThen(BiConsumer<InsnList, AbstractInsnNode> consumer) {
throw new UnsupportedOperationException();
}
}
}
Original file line number Diff line number Diff line change
@@ -4,10 +4,14 @@
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;

import java.util.function.BiConsumer;

public interface TypeAdapter {
Type from();

Type to();

void apply(InsnList list, AbstractInsnNode target);

TypeAdapter andThen(BiConsumer<InsnList, AbstractInsnNode> consumer);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package org.sinytra.adapter.patch.transformer.dynamic;

import com.mojang.datafixers.util.Pair;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.SourceInterpreter;
import org.objectweb.asm.tree.analysis.SourceValue;
import org.sinytra.adapter.patch.analysis.MethodCallAnalyzer;
import org.sinytra.adapter.patch.analysis.selector.AnnotationHandle;
import org.sinytra.adapter.patch.analysis.selector.AnnotationValueHandle;
@@ -16,7 +13,6 @@
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
import org.spongepowered.asm.mixin.injection.struct.Target;

import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
@@ -153,32 +149,4 @@ private static Pair<AbstractInsnNode, Integer> getTargetPair(ClassNode classNode
int local = discriminator.findLocal(ctx);
return Pair.of(targetInsn, local);
}

private static class MethodCallInterpreter extends SourceInterpreter {
private final MethodInsnNode targetInsn;
private List<AbstractInsnNode> targetArgs;

public MethodCallInterpreter(MethodInsnNode targetInsn) {
super(Opcodes.ASM9);
this.targetInsn = targetInsn;
}

@Nullable
public List<AbstractInsnNode> getTargetArgs() {
return this.targetArgs;
}

@Override
public SourceValue naryOperation(AbstractInsnNode insn, List<? extends SourceValue> values) {
if (insn == this.targetInsn && this.targetArgs == null) {
List<AbstractInsnNode> targetArgs = values.stream()
.map(v -> v.insns.size() == 1 ? v.insns.iterator().next() : null)
.toList();
if (!targetArgs.contains(null)) {
this.targetArgs = targetArgs;
}
}
return super.naryOperation(insn, values);
}
}
}
Original file line number Diff line number Diff line change
@@ -64,7 +64,8 @@ public FixResult apply(ClassNode classNode, MethodNode methodNode, MethodContext

if (methodContext.methodAnnotation().matchesDesc(MixinConstants.WRAP_OPERATION)) {
return handleWrapOperationToInstanceOf(cleanInjectionInsn, comparisonResult.cleanLabel(), hunkLabels, methodContext)
.or(() -> handleWrapOpertationNewInjectionPoint(cleanInjectionInsn, comparisonResult.cleanLabel(), hunkLabels, methodContext))
.or(() -> handleWrapOperationAdaptedTarget(cleanInjectionInsn, hunkLabels, methodContext))
.or(() -> handleWrapOperationNewInjectionPoint(cleanInjectionInsn, comparisonResult.cleanLabel(), hunkLabels, methodContext))
.or(() -> handleTargetModification(hunkLabels, methodContext))
.orElse(null);
}
@@ -181,7 +182,25 @@ private static FixResult handleModifyArgInjectionPoint(AbstractInsnNode cleanInj
return null;
}

private static Optional<FixResult> handleWrapOpertationNewInjectionPoint(AbstractInsnNode cleanInjectionInsn, List<AbstractInsnNode> cleanLabel, List<List<AbstractInsnNode>> hunkLabels, MethodContext methodContext) {
private static Optional<FixResult> handleWrapOperationAdaptedTarget(AbstractInsnNode cleanInjectionInsn, List<List<AbstractInsnNode>> hunkLabels, MethodContext methodContext) {
if (!(cleanInjectionInsn instanceof MethodInsnNode minsn) || hunkLabels.size() != 1) {
return Optional.empty();
}

List<MethodInsnNode> methodCalls = hunkLabels.getFirst().stream()
.filter(i -> i instanceof MethodInsnNode)
.map(i -> (MethodInsnNode) i)
.toList();
if (methodCalls.size() != 1) {
return Optional.empty();
}

Patch.Result result = WrapOperationSurgeon.tryUpgrade(methodContext, minsn, methodCalls.getLast());

return Optional.ofNullable(FixResult.of(result, PatchAuditTrail.Match.FULL));
}

private static Optional<FixResult> handleWrapOperationNewInjectionPoint(AbstractInsnNode cleanInjectionInsn, List<AbstractInsnNode> cleanLabel, List<List<AbstractInsnNode>> hunkLabels, MethodContext methodContext) {
if (!(cleanInjectionInsn instanceof MethodInsnNode minsn) || hunkLabels.size() != 1) {
return Optional.empty();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package org.sinytra.adapter.patch.transformer.dynfix;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.mojang.datafixers.util.Pair;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.sinytra.adapter.patch.analysis.MethodCallAnalyzer;
import org.sinytra.adapter.patch.analysis.locals.LocalVariableLookup;
import org.sinytra.adapter.patch.api.MethodContext;
import org.sinytra.adapter.patch.api.Patch;
import org.sinytra.adapter.patch.fixes.BytecodeFixerUpper;
import org.sinytra.adapter.patch.fixes.MethodUpgrader;
import org.sinytra.adapter.patch.fixes.TypeAdapter;
import org.sinytra.adapter.patch.transformer.operation.param.ParamTransformationUtil;
import org.sinytra.adapter.patch.transformer.pipeline.MethodTransformationPipeline;
import org.sinytra.adapter.patch.util.AdapterUtil;
import org.sinytra.adapter.patch.util.MethodQualifier;
import org.sinytra.adapter.patch.util.OpcodeUtil;

import java.util.*;
import java.util.function.Consumer;

public class WrapOperationSurgeon {

public static Patch.Result tryUpgrade(MethodContext methodContext, MethodInsnNode cleanInsn, MethodInsnNode dirtyInsn) {
MethodNode methodNode = methodContext.getMixinMethod();
LocalVariableLookup mixinLocals = new LocalVariableLookup(methodNode);

Multimap<Integer, VarInsnNode> usedVars = getUsedVars(mixinLocals, methodContext);
Map<Integer, Pair<TypeAdapter, @Nullable Consumer<InsnList>>> adapters = new HashMap<>();

for (Integer key : usedVars.keys()) {
if (key == 0) {
Pair<TypeAdapter, @Nullable Consumer<InsnList>> pair = findReplacementForInstance(cleanInsn, dirtyInsn, methodContext);
if (pair != null) {
adapters.put(key, pair);
continue;
}
}
return Patch.Result.PASS;
}

MethodQualifier oldQualifier = methodContext.getInjectionPointMethodQualifier();
String newQualifier = MethodCallAnalyzer.getCallQualifier(dirtyInsn);
return MethodTransformationPipeline.builder(b -> b.modifyInjectionPoint("INVOKE", newQualifier, false, true))
.onSuccess(() -> (c, m, mtx, ctx) -> {
MethodUpgrader.upgradeWrapOperationLayered(methodContext, oldQualifier, MethodQualifier.create(newQualifier).orElseThrow());

usedVars.forEach((i, insn) -> {
Pair<TypeAdapter, @Nullable Consumer<InsnList>> adapter = adapters.get(i);
adapter.getFirst().apply(methodNode.instructions, insn);
});

for (Pair<TypeAdapter, @Nullable Consumer<InsnList>> pair : adapters.values()) {
if (pair.getSecond() != null) {
pair.getSecond().accept(methodNode.instructions);
}
}

return Patch.Result.APPLY;
})
.apply(methodContext);
}

@Nullable
private static Pair<TypeAdapter, @Nullable Consumer<InsnList>> findReplacementForInstance(MethodInsnNode cleanInsn, MethodInsnNode dirtyInsn, MethodContext methodContext) {
MethodNode cleanTargetMethod = methodContext.findCleanInjectionTarget().methodNode();
List<AbstractInsnNode> receiverInsns = getMethodInvocationsInsns(cleanTargetMethod, cleanInsn, 0);
if (!receiverInsns.isEmpty() && receiverInsns.getFirst() instanceof VarInsnNode varInsn) {
BytecodeFixerUpper bfu = methodContext.patchContext().environment().bytecodeFixerUpper();
if (bfu == null) {
return null;
}

LocalVariableLookup cleanLookup = methodContext.cleanLocalsTable();
LocalVariableNode lvn = cleanLookup.getByIndex(varInsn.var);

TypeAdapter typeAdapter = bfu.getTypeAdapter(Type.getObjectType(dirtyInsn.owner), Type.getType(lvn.desc));
if (typeAdapter == null) {
return null;
}

List<AbstractInsnNode> subList = receiverInsns.subList(1, receiverInsns.size()).stream().map(i -> i.clone(Map.of())).toList();

TypeAdapter adapter = typeAdapter.andThen((list, insn) -> {
list.insert(insn, AdapterUtil.insnList(subList));
});
Consumer<InsnList> castCheck = subList.getFirst() instanceof TypeInsnNode typeInsn && typeInsn.getOpcode() == Opcodes.CHECKCAST ?
list -> {
List<AbstractInsnNode> originalWOCall = new ArrayList<>(ParamTransformationUtil.findWrapOperationOriginalCallArgs(methodContext.getMixinMethod(), methodContext));
// Include final method call and cast
originalWOCall.add(originalWOCall.getLast().getNext());
originalWOCall.add(originalWOCall.getLast().getNext());
originalWOCall.add(originalWOCall.getLast().getNext());
List<AbstractInsnNode> cloned = originalWOCall.stream().map(i -> i.clone(Map.of())).toList();

boolean hasLabel = list.getFirst() instanceof LabelNode;
LabelNode label = hasLabel ? (LabelNode) list.getFirst() : new LabelNode();
InsnList check = AdapterUtil.insnList(
new LabelNode(),
new VarInsnNode(Opcodes.ALOAD, 0),
new TypeInsnNode(Opcodes.INSTANCEOF, typeInsn.desc),
new JumpInsnNode(Opcodes.IFNE, label),
new LabelNode()
);
typeAdapter.apply(check, check.get(1));
check.add(AdapterUtil.insnList(cloned));
check.add(new InsnNode(OpcodeUtil.getReturnOpcode(methodContext.getMixinMethod())));
if (!hasLabel) {
check.add(label);
}

list.insert(check);
}
: null;

return Pair.of(adapter, castCheck);
}
return null;
}

@Nullable
private static List<AbstractInsnNode> getMethodInvocationsInsns(MethodNode method, MethodInsnNode minsn, int param) {
List<AbstractInsnNode> insns = new ArrayList<>();
if (getMethodInvocationsInsns(method, minsn, param, insns)) {
return insns;
}
return null;
}

@Nullable
private static boolean getMethodInvocationsInsns(MethodNode method, MethodInsnNode minsn, int param, List<AbstractInsnNode> insns) {
List<AbstractInsnNode> invocationInsns = MethodCallAnalyzer.findMethodCallParamInsns(method, minsn);
if (invocationInsns.isEmpty()) {
return false;
}
AbstractInsnNode receiver = invocationInsns.getFirst();
if (receiver instanceof VarInsnNode) {
insns.addAll(0, invocationInsns);
return true;
} else if (receiver instanceof TypeInsnNode typeInsn && receiver.getPrevious() instanceof VarInsnNode varInsn) {
insns.addFirst(typeInsn);
insns.addFirst(varInsn);
return true;
} else if (receiver instanceof MethodInsnNode invocation) {
List<AbstractInsnNode> methodInsns = new ArrayList<>();
if (getMethodInvocationsInsns(method, invocation, param, methodInsns)) {
insns.add(invocationInsns.get(param));
insns.addAll(0, methodInsns);
return true;
}
}
return false;
}

private static Multimap<Integer, VarInsnNode> getUsedVars(LocalVariableLookup mixinLocals, MethodContext methodContext) {
MethodNode methodNode = methodContext.getMixinMethod();
Type[] argsTypes = Type.getArgumentTypes(methodNode.desc);
Set<Integer> paramVars = new HashSet<>();
for (int i = 0; i < argsTypes.length; i++) {
if (argsTypes[i].equals(AdapterUtil.OPERATION_TYPE)) {
break;
}
LocalVariableNode lvn = mixinLocals.getByParameterOrdinal(i);
paramVars.add(lvn.index);
}

List<AbstractInsnNode> originalOpCall = ParamTransformationUtil.findWrapOperationOriginalCallArgs(methodNode, methodContext);
Multimap<Integer, VarInsnNode> usedVars = HashMultimap.create();
for (AbstractInsnNode insn : methodNode.instructions) {
if (insn instanceof VarInsnNode varInsn && !originalOpCall.contains(insn) && paramVars.contains(varInsn.var)) {
usedVars.put(varInsn.var, varInsn);
}
}

return usedVars;
}
}
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ public static Builder builder(MethodTransform transform) {
return new Builder(transform);
}

public static Builder builder(Consumer<MethodTransformBuilder<?>> consumer) {
public static Builder builder(Consumer<MethodTransformBuilder.Class<?>> consumer) {
BundledMethodTransform.Builder builder = BundledMethodTransform.builder();
consumer.accept(builder);
return new Builder(builder.build());
Original file line number Diff line number Diff line change
@@ -268,6 +268,22 @@ private static String toString(Textifier text) {
return sw.toString();
}

public static InsnList insnList(AbstractInsnNode... insns) {
InsnList list = new InsnList();
for (AbstractInsnNode node : insns) {
list.add(node);
}
return list;
}

public static InsnList insnList(List<AbstractInsnNode> insns) {
InsnList list = new InsnList();
for (AbstractInsnNode node : insns) {
list.add(node);
}
return list;
}

public static Type getMixinCallableReturnType(MethodNode method) {
return Type.getReturnType(method.desc) == Type.VOID_TYPE ? CI_TYPE : CIR_TYPE;
}
Original file line number Diff line number Diff line change
@@ -263,6 +263,16 @@ void testModifiedWrapOperationTarget2() throws Exception {
);
}

@Test
void testModifiedWrapOperationTarget3() throws Exception {
assertSameCode(
"org/sinytra/adapter/test/mixin/PiglinAiMixin",
"isWearingGold",
assertTargetMethod(),
assertInjectionPoint()
);
}

@Test
void testModifiedFieldType() throws Exception {
assertSameField(
Original file line number Diff line number Diff line change
@@ -66,7 +66,7 @@ protected final void assertSameCode(
.collect(Collectors.joining("\n")))
.isEqualTo(expected.parameters);

final Predicate<AbstractInsnNode> dontTest = i -> i instanceof LineNumberNode;
final Predicate<AbstractInsnNode> dontTest = i -> i instanceof LineNumberNode || i instanceof FrameNode;
Assertions.assertThat(patched.instructions.iterator())
.toIterable()
.as("Instructions")
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.sinytra.adapter.test.mixin;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.core.Holder;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.monster.piglin.PiglinAi;
import net.minecraft.world.item.ArmorItem;
import net.minecraft.world.item.ArmorMaterial;
import net.minecraft.world.item.ArmorMaterials;
import net.minecraft.world.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(PiglinAi.class)
public class PiglinAiMixin {
// https://github.com/quiqueck/BetterNether/blob/e1c5bea37001728844d16feec3ef3b3f14ae5139/src/main/java/org/betterx/betternether/mixin/common/piglin/PiglinAiMixin.java
@WrapOperation(method = "isWearingGold", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/Holder;is(Lnet/minecraft/core/Holder;)Z"))
private static boolean isWearingGold(Holder<ArmorMaterial> instance, Holder<ArmorMaterial> tHolder, Operation<Boolean> original) {
return original.call(instance, tHolder) || instance.is(ArmorMaterials.DIAMOND);
}

@WrapOperation(method = "isWearingGold", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/item/ItemStack;makesPiglinsNeutral(Lnet/minecraft/world/entity/LivingEntity;)Z"))
private static boolean isWearingGoldExpected(ItemStack instance, LivingEntity tHolder, Operation<Boolean> original) {
if (!(instance.getItem() instanceof ArmorItem)) {
return original.call(instance, tHolder);
}
return original.call(instance, tHolder) || ((ArmorItem) instance.getItem()).getMaterial().is(ArmorMaterials.DIAMOND);
}
}

0 comments on commit aade659

Please sign in to comment.