From aa2ab2fc31987efb535cba77d16766a362429ec9 Mon Sep 17 00:00:00 2001 From: LlamaLad7 Date: Mon, 3 Jun 2024 14:25:33 +0100 Subject: [PATCH] New: `@Cancellable`. --- .../mixinextras/sugar/Cancellable.java | 11 ++ .../impl/CancellableSugarApplicator.java | 115 ++++++++++++++++++ .../sugar/impl/SugarApplicator.java | 2 + .../mixinextras/utils/Decorations.java | 5 + 4 files changed, 133 insertions(+) create mode 100644 src/main/java/com/llamalad7/mixinextras/sugar/Cancellable.java create mode 100644 src/main/java/com/llamalad7/mixinextras/sugar/impl/CancellableSugarApplicator.java diff --git a/src/main/java/com/llamalad7/mixinextras/sugar/Cancellable.java b/src/main/java/com/llamalad7/mixinextras/sugar/Cancellable.java new file mode 100644 index 00000000..98fc5d06 --- /dev/null +++ b/src/main/java/com/llamalad7/mixinextras/sugar/Cancellable.java @@ -0,0 +1,11 @@ +package com.llamalad7.mixinextras.sugar; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.CLASS) +public @interface Cancellable { +} diff --git a/src/main/java/com/llamalad7/mixinextras/sugar/impl/CancellableSugarApplicator.java b/src/main/java/com/llamalad7/mixinextras/sugar/impl/CancellableSugarApplicator.java new file mode 100644 index 00000000..dd926c02 --- /dev/null +++ b/src/main/java/com/llamalad7/mixinextras/sugar/impl/CancellableSugarApplicator.java @@ -0,0 +1,115 @@ +package com.llamalad7.mixinextras.sugar.impl; + +import com.llamalad7.mixinextras.injector.StackExtension; +import com.llamalad7.mixinextras.utils.ASMUtils; +import com.llamalad7.mixinextras.utils.Decorations; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; +import org.spongepowered.asm.mixin.injection.struct.InjectionInfo; +import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode; +import org.spongepowered.asm.mixin.injection.struct.Target; + +class CancellableSugarApplicator extends SugarApplicator { + CancellableSugarApplicator(InjectionInfo info, SugarParameter parameter) { + super(info, parameter); + } + + @Override + void validate(Target target, InjectionNode node) { + } + + @Override + void prepare(Target target, InjectionNode node) { + } + + @Override + void inject(Target target, InjectionNode node, StackExtension stack) { + Type ciType = Type.getObjectType(target.getCallbackInfoClass()); + if (!ciType.equals(paramType)) { + throw new IllegalStateException( + String.format( + "@Cancellable sugar has wrong type! Expected %s but got %s!", + ciType.getClassName(), + paramType.getClassName() + ) + ); + } + int ciIndex = getOrCreateCi(target, node, stack, ciType); + stack.extra(1); + target.insns.insertBefore(node.getCurrentTarget(), new VarInsnNode(Opcodes.ALOAD, ciIndex)); + } + + @Override + int postProcessingPriority() { + // Early, we don't care about being particularly tight compared to e.g. `@Local`s. + return -1000; + } + + private int getOrCreateCi(Target target, InjectionNode node, StackExtension stack, Type ciType) { + if (node.hasDecoration(Decorations.CANCELLABLE_CI_INDEX)) { + return node.getDecoration(Decorations.CANCELLABLE_CI_INDEX); + } + int ciIndex = target.allocateLocal(); + target.addLocalVariable(ciIndex, "callbackInfo" + ciIndex, ciType.getDescriptor()); + node.decorate(Decorations.CANCELLABLE_CI_INDEX, ciIndex); + + InsnList init = new InsnList(); + init.add(new TypeInsnNode(Opcodes.NEW, ciType.getInternalName())); + init.add(new InsnNode(Opcodes.DUP)); + init.add(new LdcInsnNode(target.method.name)); + init.add(new InsnNode(Opcodes.ICONST_1)); + init.add(new MethodInsnNode( + Opcodes.INVOKESPECIAL, + ciType.getInternalName(), + "", + "(Ljava/lang/String;Z)V", + false + )); + init.add(new VarInsnNode(Opcodes.ASTORE, ciIndex)); + target.insertBefore(node, init); + stack.extra(4); + + SugarPostProcessingExtension.enqueuePostProcessing(this, () -> { + InsnList cancellation = new InsnList(); + LabelNode notCancelled = new LabelNode(); + cancellation.add(new VarInsnNode(Opcodes.ALOAD, ciIndex)); + cancellation.add(new MethodInsnNode( + Opcodes.INVOKEVIRTUAL, + ciType.getInternalName(), + "isCancelled", + "()Z", + false + )); + cancellation.add(new JumpInsnNode(Opcodes.IFEQ, notCancelled)); + cancellation.add(new VarInsnNode(Opcodes.ALOAD, ciIndex)); + if (target.returnType.equals(Type.VOID_TYPE)) { + cancellation.add(new InsnNode(Opcodes.RETURN)); + } else if (ASMUtils.isPrimitive(target.returnType)) { + cancellation.add(new MethodInsnNode( + Opcodes.INVOKEVIRTUAL, + ciType.getInternalName(), + "getReturnValue" + target.returnType.getDescriptor(), + "()" + target.returnType.getDescriptor(), + false + )); + cancellation.add(new InsnNode(target.returnType.getOpcode(Opcodes.IRETURN))); + } else { + cancellation.add(new MethodInsnNode( + Opcodes.INVOKEVIRTUAL, + ciType.getInternalName(), + "getReturnValue", + "()Ljava/lang/Object;", + false + )); + cancellation.add(new TypeInsnNode(Opcodes.CHECKCAST, target.returnType.getInternalName())); + cancellation.add(new InsnNode(Opcodes.ARETURN)); + } + cancellation.add(notCancelled); + target.insns.insert(node.getCurrentTarget(), cancellation); + // No need to adjust the stack because we only increase the height by at most 2, which is covered by + // our bump of 4 earlier. + }); + return ciIndex; + } +} diff --git a/src/main/java/com/llamalad7/mixinextras/sugar/impl/SugarApplicator.java b/src/main/java/com/llamalad7/mixinextras/sugar/impl/SugarApplicator.java index e68e2efb..e6d6e44e 100644 --- a/src/main/java/com/llamalad7/mixinextras/sugar/impl/SugarApplicator.java +++ b/src/main/java/com/llamalad7/mixinextras/sugar/impl/SugarApplicator.java @@ -2,6 +2,7 @@ import com.llamalad7.mixinextras.injector.StackExtension; import com.llamalad7.mixinextras.service.MixinExtrasService; +import com.llamalad7.mixinextras.sugar.Cancellable; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.Share; import com.llamalad7.mixinextras.utils.ASMUtils; @@ -27,6 +28,7 @@ abstract class SugarApplicator { static { List, Class>> sugars = Arrays.asList( + Pair.of(Cancellable.class, CancellableSugarApplicator.class), Pair.of(Local.class, LocalSugarApplicator.class), Pair.of(Share.class, ShareSugarApplicator.class) ); diff --git a/src/main/java/com/llamalad7/mixinextras/utils/Decorations.java b/src/main/java/com/llamalad7/mixinextras/utils/Decorations.java index a4e44722..71c38a97 100644 --- a/src/main/java/com/llamalad7/mixinextras/utils/Decorations.java +++ b/src/main/java/com/llamalad7/mixinextras/utils/Decorations.java @@ -33,4 +33,9 @@ public class Decorations { * Stores that this node has been wrapped by a {@link WrapOperation}. */ public static final String WRAPPED = "mixinextras_wrappedOperation"; + + /** + * Stores the shared CallbackInfo local index for this target instruction. + */ + public static final String CANCELLABLE_CI_INDEX = "mixinextras_cancellableCiIndex"; }