Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly add support for MixinExtras' @WrapOperation #4

Merged
merged 2 commits into from
Jan 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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