Skip to content

Commit

Permalink
New: @Cancellable.
Browse files Browse the repository at this point in the history
  • Loading branch information
LlamaLad7 committed Jun 3, 2024
1 parent e774a47 commit aa2ab2f
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/main/java/com/llamalad7/mixinextras/sugar/Cancellable.java
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
@@ -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(),
"<init>",
"(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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,6 +28,7 @@ abstract class SugarApplicator {

static {
List<Pair<Class<? extends Annotation>, Class<? extends SugarApplicator>>> sugars = Arrays.asList(
Pair.of(Cancellable.class, CancellableSugarApplicator.class),
Pair.of(Local.class, LocalSugarApplicator.class),
Pair.of(Share.class, ShareSugarApplicator.class)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}

0 comments on commit aa2ab2f

Please sign in to comment.