Skip to content

Add @AddUnusedParam #7

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

Merged
merged 6 commits into from
Jul 8, 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
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
/**
* An element of a {@link RedirectSet}
* <br/><br/>
* Specifies that any occurrence of the type {@link TypeRedirect#from()} must be replaced with the type {@link TypeRedirect#to()}.
* Specifies that any occurrence of the type {@link #from()} must be replaced with the type {@link #to()}.<br/>
* <b><u>Array types cannot be directly redirected</u></b>; instead the element type may be redirected.
* <br/><br/>
* Must be marked on a {@code class}/{@code interface} within a {@link RedirectSet} {@code interface}.
* Must be marked on a type within a {@link RedirectSet} {@code interface}.
* <br/><br/>
* The {@code class}/{@code interface} marked with {@link TypeRedirect} can optionally contain any number of {@link FieldRedirect}s and {@link MethodRedirect}
* The marked type <b><u>must</u></b> match the {@link #to()} type in terms of {@code class}/{@code interface}, ({@code abstract} doesn't matter).
* <br/><br/>
* The type marked with {@link TypeRedirect} can optionally contain any number of {@link FieldRedirect}s and {@link MethodRedirect}
*
* <br/><br/>
* By convention the marked class's name should have the format {@code FromClass_to_ToClass}, eg: {@code Object_to_String}.
* It should also be {@code abstract}
Expand All @@ -32,9 +36,13 @@
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface TypeRedirect {
/** The type to replace with {@link TypeRedirect#to()} */
/**
* The type to replace with {@link #to()}
*/
Ref from();

/** The type to replace {@link TypeRedirect#from()} with */
/**
* The type to replace {@link #from()} with
*/
Ref to();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.github.notstirred.dasm.api.annotations.transform;

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 AddUnusedParam {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.notstirred.dasm.api.provider;

import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;

import static org.objectweb.asm.Type.ARRAY;
import static org.objectweb.asm.Type.OBJECT;
Expand Down Expand Up @@ -30,7 +31,7 @@ public String mapClassName(String className) {
String mapClassName(String className);

default Type remapType(Type type) {
return Type.getObjectType(this.mapClassName(type.getClassName()).replace('.', '/'));
return Method.getMethod(this.mapClassName(type.getClassName()).replace('/', '.') + " x()").getReturnType();
}

default Type remapDescType(Type t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
import io.github.notstirred.dasm.api.annotations.redirect.redirects.AddFieldToSets;
import io.github.notstirred.dasm.api.annotations.redirect.redirects.AddMethodToSets;
import io.github.notstirred.dasm.api.annotations.redirect.redirects.AddTransformToSets;
import io.github.notstirred.dasm.api.annotations.transform.ApplicationStage;
import io.github.notstirred.dasm.api.annotations.transform.TransformFromClass;
import io.github.notstirred.dasm.api.annotations.transform.TransformFromMethod;
import io.github.notstirred.dasm.api.annotations.transform.TransformMethod;
import io.github.notstirred.dasm.api.annotations.transform.*;
import io.github.notstirred.dasm.data.ClassMethod;
import io.github.notstirred.dasm.exception.DasmException;
import io.github.notstirred.dasm.exception.NoSuchTypeExists;
Expand All @@ -24,6 +21,7 @@
import io.github.notstirred.dasm.transformer.data.MethodTransform;
import io.github.notstirred.dasm.util.ClassNodeProvider;
import io.github.notstirred.dasm.util.TypeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
Expand All @@ -49,7 +47,7 @@ public AnnotationParser(ClassNodeProvider provider) {
}

public void findRedirectSets(ClassNode targetClass) throws DasmWrappedExceptions {
Type targetClassType = Type.getType(TypeUtil.classNameToDescriptor(targetClass.name));
Type targetClassType = Type.getType(TypeUtil.typeNameToDescriptor(targetClass.name));
boolean isTargetInterface = (targetClass.access & Opcodes.ACC_INTERFACE) != 0;

DasmClassExceptions classExceptions = new DasmClassExceptions("An exception occurred when finding used redirect sets in", targetClass);
Expand Down Expand Up @@ -91,7 +89,7 @@ public void findRedirectSets(ClassNode targetClass) throws DasmWrappedExceptions
}

public Optional<ClassTransform> buildClassTarget(ClassNode targetClass) throws DasmWrappedExceptions {
Type targetType = Type.getType(TypeUtil.classNameToDescriptor(targetClass.name));
Type targetType = Type.getType(TypeUtil.typeNameToDescriptor(targetClass.name));
DasmClassExceptions classExceptions = new DasmClassExceptions("An exception occurred when looking for transforms in", targetClass);

AnnotationNode transformFromClassNode = getAnnotationIfPresent(targetClass.invisibleAnnotations, TransformFromClass.class);
Expand Down Expand Up @@ -129,7 +127,7 @@ public Optional<ClassTransform> buildClassTarget(ClassNode targetClass) throws D
}

public Optional<Collection<MethodTransform>> buildMethodTargets(ClassNode targetClass, String methodPrefix) throws DasmWrappedExceptions {
Type targetType = Type.getType(TypeUtil.classNameToDescriptor(targetClass.name));
Type targetType = Type.getType(TypeUtil.typeNameToDescriptor(targetClass.name));
boolean isTargetTypeInterface = (targetClass.access & Opcodes.ACC_INTERFACE) != 0;

DasmClassExceptions classExceptions = new DasmClassExceptions("An exception occurred when looking for transforms in", targetClass);
Expand All @@ -145,7 +143,9 @@ public Optional<Collection<MethodTransform>> buildMethodTargets(ClassNode target
List<MethodTransform> methodTransforms = new ArrayList<>();

for (MethodNode method : targetClass.methods) {
TransformMethodImpl transformMethod = parseTransformMethod(method, classExceptions);
DasmMethodExceptions methodExceptions = classExceptions.addNested(new DasmMethodExceptions(method));

TransformMethodImpl transformMethod = parseTransformMethod(method, methodExceptions);
if (transformMethod == null)
continue;

Expand All @@ -162,14 +162,17 @@ public Optional<Collection<MethodTransform>> buildMethodTargets(ClassNode target
String nonPrefixedMethodName = method.name;
String prefixedMethodName = methodPrefix + nonPrefixedMethodName;

List<AddedParameter> addedParameters = getAddedParameters(method, methodExceptions);

MethodTransform transform = new MethodTransform(
new ClassMethod(methodOwner, methodOwner, transformMethod.srcMethod()),
prefixedMethodName // We have to rename constructors because we add a prefix, and mixin expects that anything with <> is either init, or clinit
.replace("<init>", "__init__")
.replace("<clinit>", "__clinit__"),
redirectSets,
transformMethod.stage(),
transformMethod.inPlace()
transformMethod.inPlace(),
addedParameters
);

AnnotationNode addToSetsAnnotation = getAnnotationIfPresent(method.invisibleAnnotations, AddTransformToSets.class);
Expand All @@ -188,27 +191,52 @@ public Optional<Collection<MethodTransform>> buildMethodTargets(ClassNode target

methodTransforms.add(transform);
}
classExceptions.throwIfHasWrapped();
return Optional.of(methodTransforms);
}

classExceptions.throwIfHasWrapped();
return Optional.empty();
}

/**
* @param method The method to look for annotations on
* @param methodExceptions The object to add nested exceptions to if they occur
* @return The requested parameters to add to the transformed method in order.
*/
@NotNull
private static List<AddedParameter> getAddedParameters(MethodNode method, DasmMethodExceptions methodExceptions) {
List<AddedParameter> addedParameters = new ArrayList<>();

List<AnnotationNode>[] invisibleParameterAnnotations = method.invisibleParameterAnnotations;
if (invisibleParameterAnnotations == null)
return addedParameters;

Type[] argumentTypes = Type.getArgumentTypes(method.desc);
for (int i = 0; i < invisibleParameterAnnotations.length; i++) {
List<AnnotationNode> invisibleParameterAnnotation = invisibleParameterAnnotations[i];
if (invisibleParameterAnnotation != null) {
if (AnnotationUtil.isAnnotationPresent(invisibleParameterAnnotation, AddUnusedParam.class)) {
addedParameters.add(new AddedParameter(argumentTypes[i], i));
}
}
}
return addedParameters;
}

/**
* @param method The method on which to look for annotations
* @param classExceptions The object to add nested exceptions to if they occur
* @param methodExceptions The object to add nested exceptions to if they occur
* @return null if there was no annotation or there was an exception
*/
@Nullable
private static TransformMethodImpl parseTransformMethod(MethodNode method, DasmClassExceptions classExceptions) {
private static TransformMethodImpl parseTransformMethod(MethodNode method, DasmMethodExceptions methodExceptions) {
TransformMethodImpl transformMethod;
AnnotationNode transformFromMethodAnnotation = getAnnotationIfPresent(method.invisibleAnnotations, TransformFromMethod.class);
AnnotationNode transformMethodAnnotation = getAnnotationIfPresent(method.invisibleAnnotations, TransformMethod.class);
if (transformFromMethodAnnotation == null && transformMethodAnnotation == null) {
return null;
}
DasmMethodExceptions methodExceptions = classExceptions.addNested(new DasmMethodExceptions(method));
if (transformFromMethodAnnotation != null && transformMethodAnnotation != null) {
methodExceptions.addException(new BothTransformMethodAndTransformFromMethodPresent(method));
return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.github.notstirred.dasm.annotation;

import com.google.common.collect.Lists;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
Expand Down Expand Up @@ -36,7 +37,21 @@ public static AnnotationNode getAnnotationIfPresent(List<AnnotationNode> annotat
return null;
}

public boolean isAnnotationIfPresent(List<AnnotationNode> annotations, Class<?> annotation) {
@NotNull
public static List<AnnotationNode> getAllAnnotations(List<AnnotationNode> annotations, Class<?> annotation) {
List<AnnotationNode> annotationsOfType = new ArrayList<>();
if (annotations == null) {
return annotationsOfType;
}
for (AnnotationNode annotationNode : annotations) {
if (annotationNode.desc.equals(classToDescriptor(annotation))) {
annotationsOfType.add(annotationNode);
}
}
return annotationsOfType;
}

public static boolean isAnnotationPresent(List<AnnotationNode> annotations, Class<?> annotation) {
if (annotations == null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.notstirred.dasm.annotation.parse;

import lombok.Value;
import org.objectweb.asm.Type;

@Value
public class AddedParameter {
Type type;
int index;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import java.util.Map;
import java.util.Optional;

import static io.github.notstirred.dasm.util.TypeUtil.classNameToDescriptor;
import static io.github.notstirred.dasm.util.TypeUtil.typeNameToDescriptor;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class RefImpl {
Expand All @@ -24,7 +24,7 @@ public static Type parseRefAnnotation(String name, Map<String, Object> outerValu
if (values.containsKey("string")) {
String string = (String) values.get("string");
if (!string.isEmpty()) {
type = Type.getType(classNameToDescriptor(string));
type = Type.getType(typeNameToDescriptor(string));
}
}
if (type == null || type.getClassName().equals(Ref.EmptyRef.class.getName())) {
Expand All @@ -43,7 +43,7 @@ public static Type parseUnnamedRefAnnotation(AnnotationNode annotationNode) thro
if (values.containsKey("string")) {
String string = (String) values.get("string");
if (!string.isEmpty()) {
type = Type.getType(classNameToDescriptor(string));
type = Type.getType(typeNameToDescriptor(string));
}
}
if (type == null || type.getClassName().equals(Ref.EmptyRef.class.getName())) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package io.github.notstirred.dasm.transformer;

import io.github.notstirred.dasm.annotation.parse.AddedParameter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.MethodNode;

import java.util.Arrays;
import java.util.List;

import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ASM9;

public class ParameterAdder extends MethodVisitor {
private final int addedParameterTypeSize; // longs and doubles take up two spaces in the LVT for no reason whatsoever.
private final boolean isStatic;
private final int idxOfFirstLocal;
private final int[] parameterIndicesLUT;

private final String newMethodDescriptor;
private final Type owner;

private int maxStack;
private int maxLocals;

private Label startLabel;
private Label endLabel;

public ParameterAdder(MethodNode node, Type owner, String originalMethodDescriptor, String newMethodDescriptor, List<AddedParameter> addedParameters) {
super(ASM9, node);
this.owner = owner;
this.isStatic = (node.access & ACC_STATIC) != 0;
int lvtSize = Arrays.stream(Type.getArgumentTypes(originalMethodDescriptor)).mapToInt(Type::getSize).sum() + (this.isStatic ? 0 : 1);
this.idxOfFirstLocal = lvtSize;
this.newMethodDescriptor = newMethodDescriptor;
this.parameterIndicesLUT = new int[lvtSize];

for (int i = 0; i < parameterIndicesLUT.length; i++) {
parameterIndicesLUT[i] = i;
}
int addedParamSize = 0;
for (AddedParameter addedParameter : addedParameters) {
addedParamSize += addedParameter.type().getSize();
for (int i = addedParameter.index() + (this.isStatic ? 0 : 1); i < parameterIndicesLUT.length; i += addedParameter.type().getSize()) {
parameterIndicesLUT[i]++;
}
}
this.addedParameterTypeSize = addedParamSize;
}

@Override
public void visitCode() {
super.visitCode();

Type[] methodArgs = Type.getArgumentTypes(this.newMethodDescriptor);

endLabel = new Label();
startLabel = new Label();
super.visitLabel(startLabel);

int localIdx = 0;
if (!this.isStatic) {
super.visitLocalVariable("this", this.owner.getDescriptor(), null, startLabel, endLabel, localIdx++);
}
for (Type methodArg : methodArgs) {
super.visitLocalVariable("param" + localIdx, methodArg.getDescriptor(), null, startLabel, endLabel, localIdx);
localIdx += methodArg.getSize();
}
}

@Override
public void visitVarInsn(int opcode, int varIndex) {
if (varIndex >= idxOfFirstLocal) { // a local variable
super.visitVarInsn(opcode, varIndex + this.addedParameterTypeSize);
} else { // a parameter
super.visitVarInsn(opcode, this.parameterIndicesLUT[varIndex]);
}
}

@Override
public void visitIincInsn(int varIndex, int increment) {
if (varIndex >= idxOfFirstLocal) { // a local variable
super.visitIincInsn(varIndex + this.addedParameterTypeSize, increment);
} else { // a parameter
super.visitIincInsn(this.parameterIndicesLUT[varIndex], increment);
}
}

@Override
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
if (index >= this.idxOfFirstLocal) {
super.visitLocalVariable(name, descriptor, signature, start, end, index + this.addedParameterTypeSize);
}
}

@Override
public void visitMaxs(int maxStack, int maxLocals) {
this.maxStack = maxStack;
this.maxLocals = maxLocals;
}

@Override
public void visitEnd() {
super.visitMaxs(this.maxStack, this.maxLocals + this.addedParameterTypeSize);

super.visitLabel(this.endLabel);

super.visitEnd();
}
}
Loading
Loading