Skip to content

Commit

Permalink
Properly add support for MixinExtras' @WrapOperation (#4)
Browse files Browse the repository at this point in the history
* Work on wrap support

* More work
  • Loading branch information
Matyrobbrt authored Jan 24, 2024
1 parent 20962f8 commit 2ff69b9
Show file tree
Hide file tree
Showing 19 changed files with 557 additions and 45 deletions.
2 changes: 1 addition & 1 deletion definition/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ dependencies {
implementation(group = "org.slf4j", "slf4j-api", "2.0.0")
"testClassesImplementation"(implementation(group = "net.fabricmc", name = "sponge-mixin", version = "0.12.5+mixin.0.8.5"))
compileOnly(group = "org.jetbrains", name = "annotations", version = "24.0.1")
implementation(group = "io.github.llamalad7", name = "mixinextras-common", version = "0.3.1")
"testClassesImplementation"(implementation(group = "io.github.llamalad7", name = "mixinextras-common", version = "0.3.1"))

api(platform("org.ow2.asm:asm-bom:9.5"))
api(group = "org.ow2.asm", name = "asm")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ public void updateDescription(ClassNode classNode, MethodNode methodNode, List<T
methodNode.signature = null;
}

@Override
public boolean isStatic(MethodNode methodNode) {
return (methodNode.access & Opcodes.ACC_STATIC) != 0;
}

@Override
public @Nullable List<LocalVariable> getTargetMethodLocals(ClassNode classNode, MethodNode methodNode, ClassNode targetClass, MethodNode targetMethod) {
Type[] targetParams = Type.getArgumentTypes(targetMethod.desc);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import dev.su5ed.sinytra.adapter.patch.selector.AnnotationHandle;
import dev.su5ed.sinytra.adapter.patch.selector.AnnotationValueHandle;
import dev.su5ed.sinytra.adapter.patch.transformer.*;
import dev.su5ed.sinytra.adapter.patch.transformer.param.TransformParameters;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
Expand Down Expand Up @@ -173,6 +174,13 @@ public T modifyParams(Consumer<ModifyMethodParams.Builder> consumer) {
return transform(builder.build());
}

@Override
public T transformParams(Consumer<TransformParameters.Builder> consumer) {
final var builder = new TransformParameters.Builder();
consumer.accept(builder);
return transform(builder.build());
}

@Override
public T modifyTarget(String... methods) {
return transform(new ModifyInjectionTarget(List.of(methods)));
Expand Down Expand Up @@ -226,6 +234,12 @@ public T transform(MethodTransform transformer) {
return coerce();
}

@Override
public T transformMethods(List<MethodTransform> transformers) {
transformers.forEach(this::transform);
return coerce();
}

@Override
public T chain(Consumer<T> consumer) {
consumer.accept(coerce());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.mojang.datafixers.util.Pair;
import dev.su5ed.sinytra.adapter.patch.api.MethodTransform;
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams;
import dev.su5ed.sinytra.adapter.patch.transformer.param.InjectParameterTransform;
import dev.su5ed.sinytra.adapter.patch.transformer.param.ParameterTransformer;
import dev.su5ed.sinytra.adapter.patch.transformer.param.TransformParameters;
import dev.su5ed.sinytra.adapter.patch.util.AdapterUtil;
import dev.su5ed.sinytra.adapter.patch.util.GeneratedVariables;
import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap;
Expand All @@ -23,7 +29,24 @@
public record ParametersDiff(int originalCount, List<Pair<Integer, Type>> insertions, List<Pair<Integer, Type>> replacements, List<Pair<Integer, Integer>> swaps,
List<Integer> removals, List<Pair<Integer, Integer>> moves) {
public static final ParametersDiff EMPTY = new ParametersDiff(-1, List.of(), List.of(), List.of(), List.of(), List.of());


public List<MethodTransform> createTransforms(ModifyMethodParams.TargetType type) {
final var list = new ArrayList<MethodTransform>();
var light = ModifyMethodParams.ParamsContext.createLight(this);
if (!light.isEmpty()) {
list.add(new ModifyMethodParams(
light,
type, false, null
));
}
if (!insertions.isEmpty()) {
list.add(new TransformParameters(insertions.stream()
.<ParameterTransformer>map(p -> new InjectParameterTransform(p.getFirst(), p.getSecond()))
.toList(), true));
}
return list;
}

public record MethodParameter(Type type, boolean isGeneratedType) {
public MethodParameter(@Nullable String name, Type type) {
this(type, name != null && GeneratedVariables.isGeneratedVariableName(name, type));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public interface MethodContext {

void updateDescription(ClassNode classNode, MethodNode methodNode, List<Type> parameters);

boolean isStatic(MethodNode methodNode);

@Nullable
List<LocalVariable> getTargetMethodLocals(ClassNode classNode, MethodNode methodNode, ClassNode targetClass, MethodNode targetMethod);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class MixinConstants {
// Mixinextras annotations
public static final String MODIFY_EXPR_VAL = "Lcom/llamalad7/mixinextras/injector/ModifyExpressionValue;";
public static final String WRAP_OPERATION = "Lcom/llamalad7/mixinextras/injector/wrapoperation/WrapOperation;";
public static final String OPERATION_INTERNAL_NAME = "com/llamalad7/mixinextras/injector/wrapoperation/Operation";
// Misc
public static final String MIXIN = "Lorg/spongepowered/asm/mixin/Mixin;";
public static final String AT = "Lorg/spongepowered/asm/mixin/injection/At;";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodAccess;
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams;
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMixinType;
import dev.su5ed.sinytra.adapter.patch.transformer.param.TransformParameters;
import org.jetbrains.annotations.ApiStatus;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.InstructionAdapter;
import org.objectweb.asm.tree.ClassNode;
Expand Down Expand Up @@ -60,6 +62,9 @@ interface Builder<T extends Builder<T>> {

T modifyParams(Consumer<ModifyMethodParams.Builder> consumer);

@ApiStatus.Experimental
T transformParams(Consumer<TransformParameters.Builder> consumer);

T modifyTarget(String... methods);

T modifyTarget(ModifyInjectionTarget.Action action, String... methods);
Expand All @@ -80,6 +85,8 @@ interface Builder<T extends Builder<T>> {

T transform(MethodTransform transformer);

T transformMethods(List<MethodTransform> transformers);

T chain(Consumer<T> consumer);

PatchInstance build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
package dev.su5ed.sinytra.adapter.patch.fixes;

import com.google.common.collect.ImmutableList;
import dev.su5ed.sinytra.adapter.patch.analysis.EnhancedParamsDiff;
import dev.su5ed.sinytra.adapter.patch.analysis.ParametersDiff;
import dev.su5ed.sinytra.adapter.patch.api.MethodContext;
import dev.su5ed.sinytra.adapter.patch.api.MixinConstants;
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyArgsOffsetTransformer;
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams;
import dev.su5ed.sinytra.adapter.patch.util.MethodQualifier;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

import java.util.List;

public final class MethodUpgrader {

public static void upgradeMethod(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, String originalDesc, String modifiedDesc) {
Expand All @@ -27,31 +20,7 @@ public static void upgradeMethod(ClassNode classNode, MethodNode methodNode, Met
}
if (methodContext.methodAnnotation().matchesDesc(MixinConstants.MODIFY_ARGS)) {
ModifyArgsOffsetTransformer.handleModifiedDesc(methodNode, cleanQualifier.desc(), dirtyQualifier.desc());
} else if (methodContext.methodAnnotation().matchesDesc(MixinConstants.WRAP_OPERATION)) {
upgradeWrapOperation(classNode, methodNode, methodContext, cleanQualifier, dirtyQualifier);
}
}

private static void upgradeWrapOperation(ClassNode classNode, MethodNode methodNode, 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()));
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
ParametersDiff diff = EnhancedParamsDiff.create(originalDesc, modifiedDesc);
if (!diff.isEmpty()) {
ModifyMethodParams patch = ModifyMethodParams.create(diff, ModifyMethodParams.TargetType.ALL);
patch.apply(classNode, methodNode, methodContext, methodContext.patchContext());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import dev.su5ed.sinytra.adapter.patch.api.MethodTransform;
import dev.su5ed.sinytra.adapter.patch.transformer.*;
import dev.su5ed.sinytra.adapter.patch.transformer.filter.InjectionPointTransformerFilter;
import dev.su5ed.sinytra.adapter.patch.transformer.param.TransformParameters;

import java.util.Objects;

Expand All @@ -27,6 +28,7 @@ public class MethodTransformSerialization {
.put("modify_injection_target", ModifyInjectionTarget.CODEC)
.put("modfiy_access", ModifyMethodAccess.CODEC)
.put("modify_method", ModifyMethodParams.CODEC)
.put("transform_parameters", TransformParameters.CODEC)
.put("soft_modify_method", SoftMethodParamsPatch.CODEC)
.put("injection_point_filter", InjectionPointTransformerFilter.CODEC)
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ private static void offsetLVT(MethodNode methodNode, int lvtIndex, int offset) {
}
}

private static void offsetParameters(MethodNode methodNode, int paramIndex) {
public static void offsetParameters(MethodNode methodNode, int paramIndex) {
if (methodNode.invisibleParameterAnnotations != null) {
List<List<AnnotationNode>> annotations = new ArrayList<>(Arrays.asList(methodNode.invisibleParameterAnnotations));
if (paramIndex < annotations.size()) {
Expand Down Expand Up @@ -403,7 +403,7 @@ private static List<AbstractInsnNode> findWrapOperationOriginalCall(MethodNode m
public enum TargetType {
ALL,
METHOD(MixinConstants.INJECT, MixinConstants.OVERWRITE, MixinConstants.MODIFY_VAR),
INJECTION_POINT(MixinConstants.REDIRECT, MixinConstants.MODIFY_ARG, MixinConstants.MODIFY_ARGS);
INJECTION_POINT(MixinConstants.REDIRECT, MixinConstants.MODIFY_ARG, MixinConstants.MODIFY_ARGS, MixinConstants.WRAP_OPERATION);

public static final Codec<TargetType> CODEC = Codec.STRING.xmap(TargetType::from, TargetType::name);

Expand Down Expand Up @@ -454,6 +454,10 @@ public static ParamsContext create(ParametersDiff diff) {
return new ParamsContext(diff.insertions(), diff.replacements(), diff.swaps(), List.of(), diff.removals(), diff.moves(), List.of());
}

public static ParamsContext createLight(ParametersDiff diff) {
return new ParamsContext(List.of(), diff.replacements(), diff.swaps(), List.of(), diff.removals(), diff.moves(), List.of());
}

public boolean isEmpty() {
return this.insertions.isEmpty() && this.replacements.isEmpty() && this.swaps.isEmpty() && this.substitutes.isEmpty() && this.removals.isEmpty() && this.moves.isEmpty() && this.inlines.isEmpty();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package dev.su5ed.sinytra.adapter.patch.transformer.param;

import com.mojang.datafixers.util.Pair;
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.MixinConstants;
import dev.su5ed.sinytra.adapter.patch.api.Patch;
import dev.su5ed.sinytra.adapter.patch.api.PatchContext;
import dev.su5ed.sinytra.adapter.patch.selector.AnnotationHandle;
import dev.su5ed.sinytra.adapter.patch.transformer.ModifyArgsOffsetTransformer;
import dev.su5ed.sinytra.adapter.patch.util.AdapterUtil;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.ParameterNode;
import org.objectweb.asm.tree.VarInsnNode;

import java.util.List;

import static dev.su5ed.sinytra.adapter.patch.transformer.ModifyMethodParams.offsetParameters;

public record InjectParameterTransform(int index, Type type) implements ParameterTransformer {
static final Codec<InjectParameterTransform> CODEC = RecordCodecBuilder.create(in -> in.group(
Codec.intRange(0, 255).fieldOf("index").forGetter(InjectParameterTransform::index),
AdapterUtil.TYPE_CODEC.fieldOf("parameterType").forGetter(InjectParameterTransform::type)
).apply(in, InjectParameterTransform::new));

@Override
public Patch.Result apply(ClassNode classNode, MethodNode methodNode, MethodContext methodContext, PatchContext context, List<Type> parameters, int offset) {
boolean isNonStatic = (methodNode.access & Opcodes.ACC_STATIC) == 0;
final int index = this.index + offset;
if (index >= parameters.size() + 1) {
return Patch.Result.PASS;
}

AnnotationHandle annotation = methodContext.methodAnnotation();

if (annotation.matchesDesc(MixinConstants.MODIFY_VAR)) {
annotation.<Integer>getValue("index").ifPresent(indexHandle -> {
int indexValue = indexHandle.get();
if (indexValue >= index) {
indexHandle.set(indexValue + 1);
}
});
return Patch.Result.APPLY;
}

if (annotation.matchesDesc(MixinConstants.MODIFY_ARGS)) {
ModifyArgsOffsetTransformer.modify(methodNode, List.of(Pair.of(this.index, this.type)));
return Patch.Result.APPLY;
}

LocalVariableNode self = methodNode.localVariables.stream().filter(lvn -> lvn.index == 0).findFirst().orElseThrow();

int lvtIndex = ParameterTransformer.calculateLVTIndex(parameters, isNonStatic, index);

withLVTSnapshot(methodNode, () -> {
ParameterNode newParameter = new ParameterNode("adapter_injected_" + index, Opcodes.ACC_SYNTHETIC);
parameters.add(index, type);
methodNode.parameters.add(index, newParameter);

offsetParameters(methodNode, index);

methodNode.localVariables.add(index + (isNonStatic ? 1 : 0), new LocalVariableNode(newParameter.name, type.getDescriptor(), null, self.start, self.end, lvtIndex));
});

extractWrapOperation(methodContext, methodNode, parameters, wrapOpModification -> wrapOpModification
.insertParameter(index, nodes -> nodes.add(new VarInsnNode(Opcodes.ALOAD, lvtIndex))));

return Patch.Result.APPLY;
}

@Override
public Codec<? extends ParameterTransformer> codec() {
return CODEC;
}
}
Loading

0 comments on commit 2ff69b9

Please sign in to comment.