-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inline, swap, substitution as param transformers
- Loading branch information
1 parent
afc16b7
commit f2dc31c
Showing
11 changed files
with
273 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
...in/java/dev/su5ed/sinytra/adapter/patch/transformer/param/InlineParameterTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package dev.su5ed.sinytra.adapter.patch.transformer.param; | ||
|
||
import com.mojang.logging.LogUtils; | ||
import dev.su5ed.sinytra.adapter.patch.api.MethodContext; | ||
import dev.su5ed.sinytra.adapter.patch.api.Patch; | ||
import dev.su5ed.sinytra.adapter.patch.api.PatchContext; | ||
import dev.su5ed.sinytra.adapter.patch.util.AdapterUtil; | ||
import org.objectweb.asm.Type; | ||
import org.objectweb.asm.commons.InstructionAdapter; | ||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.InsnList; | ||
import org.objectweb.asm.tree.LocalVariableNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import org.objectweb.asm.tree.VarInsnNode; | ||
import org.slf4j.Logger; | ||
|
||
import java.util.Comparator; | ||
import java.util.List; | ||
import java.util.function.Consumer; | ||
|
||
import static dev.su5ed.sinytra.adapter.patch.PatchInstance.MIXINPATCH; | ||
|
||
public record InlineParameterTransformer(int target, Consumer<InstructionAdapter> adapter) implements ParameterTransformer { | ||
private static final Logger LOGGER = LogUtils.getLogger(); | ||
|
||
@Override | ||
public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, PatchContext context, List<Type> parameters, int offset) { | ||
final int index = this.target + offset; | ||
LOGGER.info(MIXINPATCH, "Inlining parameter {} of method {}.{}", index, classNode.name, methodNode.name); | ||
final int replaceIndex = -999 + index; | ||
|
||
withLVTSnapshot(methodNode, () -> { | ||
if (index < methodNode.parameters.size()) { | ||
methodNode.parameters.remove(index); | ||
} | ||
|
||
methodNode.localVariables.sort(Comparator.comparingInt(lvn -> lvn.index)); | ||
LocalVariableNode lvn = methodNode.localVariables.remove(index + (methodContext.isStatic(methodNode) ? 0 : 1)); | ||
AdapterUtil.replaceLVT(methodNode, idx -> idx == lvn.index ? replaceIndex : idx); | ||
}); | ||
|
||
parameters.remove(index); | ||
|
||
for (AbstractInsnNode insn : methodNode.instructions) { | ||
if (insn instanceof VarInsnNode varInsn && varInsn.var == replaceIndex) { | ||
InsnList replacementInsns = AdapterUtil.insnsWithAdapter(adapter); | ||
methodNode.instructions.insert(varInsn, replacementInsns); | ||
methodNode.instructions.remove(varInsn); | ||
} | ||
} | ||
|
||
return Patch.Result.COMPUTE_FRAMES; | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
...ava/dev/su5ed/sinytra/adapter/patch/transformer/param/SubstituteParameterTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package dev.su5ed.sinytra.adapter.patch.transformer.param; | ||
|
||
import com.mojang.logging.LogUtils; | ||
import com.mojang.serialization.Codec; | ||
import com.mojang.serialization.codecs.RecordCodecBuilder; | ||
import dev.su5ed.sinytra.adapter.patch.api.MethodContext; | ||
import dev.su5ed.sinytra.adapter.patch.api.Patch; | ||
import dev.su5ed.sinytra.adapter.patch.api.PatchContext; | ||
import dev.su5ed.sinytra.adapter.patch.util.AdapterUtil; | ||
import org.objectweb.asm.Type; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import org.slf4j.Logger; | ||
|
||
import java.util.List; | ||
|
||
public record SubstituteParameterTransformer(int target, int substitute) implements ParameterTransformer { | ||
static final Codec<SubstituteParameterTransformer> CODEC = RecordCodecBuilder.create(in -> in.group( | ||
Codec.intRange(0, 255).fieldOf("target").forGetter(SubstituteParameterTransformer::target), | ||
Codec.intRange(0, 255).fieldOf("substitute").forGetter(SubstituteParameterTransformer::substitute) | ||
).apply(in, SubstituteParameterTransformer::new)); | ||
|
||
private static final Logger LOGGER = LogUtils.getLogger(); | ||
|
||
@Override | ||
public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, PatchContext context, List<Type> parameters, int offset) { | ||
final int paramIndex = this.target + offset; | ||
final int substituteParamIndex = this.substitute + offset; | ||
final boolean isNonStatic = !methodContext.isStatic(methodNode); | ||
final int localIndex = ParameterTransformer.calculateLVTIndex(parameters, isNonStatic, paramIndex); | ||
|
||
if (methodNode.parameters.size() <= paramIndex) { | ||
return Patch.Result.PASS; | ||
} | ||
|
||
withLVTSnapshot(methodNode, () -> { | ||
LOGGER.info("Substituting parameter {} for {} in {}.{}", paramIndex, substituteParamIndex, classNode.name, methodNode.name); | ||
parameters.remove(paramIndex); | ||
methodNode.parameters.remove(paramIndex); | ||
methodNode.localVariables.removeIf(lvn -> lvn.index == localIndex); | ||
|
||
final int substituteIndex = ParameterTransformer.calculateLVTIndex(parameters, isNonStatic, substituteParamIndex); | ||
AdapterUtil.replaceLVT(methodNode, idx -> idx == localIndex ? substituteIndex : idx); | ||
}); | ||
|
||
return Patch.Result.COMPUTE_FRAMES; | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...ain/java/dev/su5ed/sinytra/adapter/patch/transformer/param/SwapParametersTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package dev.su5ed.sinytra.adapter.patch.transformer.param; | ||
|
||
import com.mojang.logging.LogUtils; | ||
import com.mojang.serialization.Codec; | ||
import com.mojang.serialization.codecs.RecordCodecBuilder; | ||
import dev.su5ed.sinytra.adapter.patch.api.MethodContext; | ||
import dev.su5ed.sinytra.adapter.patch.api.Patch; | ||
import dev.su5ed.sinytra.adapter.patch.api.PatchContext; | ||
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams; | ||
import org.objectweb.asm.Type; | ||
import org.objectweb.asm.tree.ClassNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import org.objectweb.asm.tree.ParameterNode; | ||
import org.slf4j.Logger; | ||
|
||
import java.util.List; | ||
|
||
import static dev.su5ed.sinytra.adapter.patch.PatchInstance.MIXINPATCH; | ||
|
||
public record SwapParametersTransformer(int from, int to) implements ParameterTransformer { | ||
private static final Logger LOGGER = LogUtils.getLogger(); | ||
|
||
static final Codec<SwapParametersTransformer> CODEC = RecordCodecBuilder.create(in -> in.group( | ||
Codec.intRange(0, 255).fieldOf("from").forGetter(SwapParametersTransformer::from), | ||
Codec.intRange(0, 255).fieldOf("to").forGetter(SwapParametersTransformer::to) | ||
).apply(in, SwapParametersTransformer::new)); | ||
|
||
@Override | ||
public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, PatchContext context, List<Type> parameters, int offset) { | ||
int from = offset + this.from; | ||
int to = offset + this.to; | ||
boolean nonStatic = !methodContext.isStatic(methodNode); | ||
ParameterNode fromNode = methodNode.parameters.get(from); | ||
ParameterNode toNode = methodNode.parameters.get(to); | ||
|
||
int fromOldLVT = ParameterTransformer.calculateLVTIndex(parameters, nonStatic, from); | ||
int toOldLVT = ParameterTransformer.calculateLVTIndex(parameters, nonStatic, to); | ||
|
||
Type fromType = parameters.get(from); | ||
Type toType = parameters.get(to); | ||
parameters.set(from, toType); | ||
parameters.set(to, fromType); | ||
|
||
methodNode.parameters.set(from, toNode); | ||
methodNode.parameters.set(to, fromNode); | ||
|
||
LOGGER.info(MIXINPATCH, "Swapped parameters at positions {}({}) and {}({}) in {}.{}", from, fromNode.name, to, toNode.name, classNode.name, methodNode.name); | ||
|
||
int fromNewLVT = ParameterTransformer.calculateLVTIndex(parameters, nonStatic, from); | ||
int toNewLVT = ParameterTransformer.calculateLVTIndex(parameters, nonStatic, to); | ||
|
||
// Account for "big" LVT variables (like longs and doubles) | ||
// Uses of the old parameter need to be the new parameter and vice versa | ||
ModifyMethodParams.swapLVT(methodNode, fromOldLVT, toNewLVT) | ||
.andThen(ModifyMethodParams.swapLVT(methodNode, toOldLVT, fromNewLVT)) | ||
.accept(null); | ||
|
||
return Patch.Result.COMPUTE_FRAMES; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
definition/src/test/java/dev/su5ed/sinytra/adapter/patch/test/mixin/ParameterInlineTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package dev.su5ed.sinytra.adapter.patch.test.mixin; | ||
|
||
import dev.su5ed.sinytra.adapter.patch.api.MixinConstants; | ||
import dev.su5ed.sinytra.adapter.patch.api.Patch; | ||
import org.junit.jupiter.api.Test; | ||
|
||
public class ParameterInlineTest extends MixinPatchTest { | ||
@Test | ||
void testSimpleInline() throws Exception { | ||
assertSameCode( | ||
"org/sinytra/adapter/test/mixins/ParameterInlineMixin", | ||
"testSimpleInline", | ||
Patch.builder() | ||
.targetInjectionPoint("") | ||
.targetMethod("simple") | ||
.targetMixinType(MixinConstants.INJECT) | ||
.transformParams(params -> params.inline(3, adapter -> adapter.visitLdcInsn("inlined"))) | ||
); | ||
} | ||
|
||
@Test | ||
void testBigInline() throws Exception { | ||
assertSameCode( | ||
"org/sinytra/adapter/test/mixins/ParameterInlineMixin", | ||
"testBigInline", | ||
Patch.builder() | ||
.targetInjectionPoint("") | ||
.targetMethod("big") | ||
.targetMixinType(MixinConstants.INJECT) | ||
.transformParams(params -> params.inline(2, adapter -> adapter.visitLdcInsn(43.0d))) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
definition/src/testClasses/java/org/sinytra/adapter/test/classes/ParameterInline.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.sinytra.adapter.test.classes; | ||
|
||
public class ParameterInline { | ||
public static void simple(int p1, String p2, int p3) { | ||
|
||
} | ||
|
||
public static void big(int p1, double p2, int p3) { | ||
|
||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
definition/src/testClasses/java/org/sinytra/adapter/test/mixins/ParameterInlineMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package org.sinytra.adapter.test.mixins; | ||
|
||
import org.sinytra.adapter.test.classes.ParameterInline; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
|
||
@Mixin(ParameterInline.class) | ||
public class ParameterInlineMixin { | ||
@Inject(method = "simple(ILjava/lang/String;ILjava/lang/String;)V", at = @At("HEAD")) | ||
private void testSimpleInline(int p1, String p2, int p3, String p4) { | ||
System.out.println(p4); | ||
} | ||
|
||
private void testSimpleInlineExpected(int p1, String p2, int p3) { | ||
System.out.println("inlined"); | ||
} | ||
|
||
@Inject(method = "big(IDI)V", at = @At("HEAD")) | ||
private void testBigInline(int p1, double p2, double p3, int p4) { | ||
System.out.println(p3 * p2); | ||
System.out.println("p4: " + p4); | ||
} | ||
|
||
private void testBigInlineExpected(int p1, double p2, int p4) { | ||
System.out.println(43d * p2); | ||
System.out.println("p4: " + p4); | ||
} | ||
} |