Skip to content

Commit a676855

Browse files
committed
Rewrite AddUnusedParam and ParameterAdder to allow an index to be specified
1 parent d5b85ab commit a676855

File tree

14 files changed

+523
-47
lines changed

14 files changed

+523
-47
lines changed

src/api/java/io/github/notstirred/dasm/api/annotations/transform/AddUnusedParam.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,31 @@
22

33
import io.github.notstirred.dasm.api.annotations.selector.Ref;
44

5-
import java.lang.annotation.ElementType;
6-
import java.lang.annotation.Retention;
7-
import java.lang.annotation.RetentionPolicy;
8-
import java.lang.annotation.Target;
5+
import java.lang.annotation.*;
96

107
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
118
@Retention(RetentionPolicy.CLASS)
9+
@Repeatable(AddUnusedParam.List.class)
1210
public @interface AddUnusedParam {
1311
Ref type();
12+
13+
int index();
14+
15+
/**
16+
* A wrapper annotation that makes the {@link AddUnusedParam} annotation repeatable.
17+
*
18+
* <p>Programmers generally do not need to write this. It is created by Java when a programmer
19+
* writes more than one {@link AddUnusedParam} annotation at the same location.
20+
*/
21+
@Documented
22+
@Retention(RetentionPolicy.RUNTIME)
23+
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
24+
@interface List {
25+
/**
26+
* Return the repeatable annotations.
27+
*
28+
* @return the repeatable annotations
29+
*/
30+
AddUnusedParam[] value();
31+
}
1432
}

src/main/java/io/github/notstirred/dasm/annotation/AnnotationParser.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
import java.util.*;
3434
import java.util.stream.Collectors;
3535

36-
import static io.github.notstirred.dasm.annotation.AnnotationUtil.*;
36+
import static io.github.notstirred.dasm.annotation.AnnotationUtil.getAnnotationIfPresent;
37+
import static io.github.notstirred.dasm.annotation.AnnotationUtil.getAnnotationValues;
3738
import static org.objectweb.asm.Opcodes.ACC_INTERFACE;
3839
import static org.objectweb.asm.Opcodes.ACC_STATIC;
3940

@@ -205,13 +206,24 @@ public Optional<Collection<MethodTransform>> buildMethodTargets(ClassNode target
205206
*/
206207
@NotNull
207208
private static List<AddedParameter> getAddedParameters(MethodNode method, DasmMethodExceptions methodExceptions) {
208-
List<AnnotationNode> addUnusedParamAnnotations = getAllAnnotations(method.invisibleAnnotations, AddUnusedParam.class);
209209
List<AddedParameter> addedParameters = new ArrayList<>();
210-
for (AnnotationNode annotation : addUnusedParamAnnotations) {
211-
try {
212-
addedParameters.add(AddedParameter.parse(annotation));
213-
} catch (RefImpl.RefAnnotationGivenNoArguments e) {
214-
methodExceptions.addException(e);
210+
AnnotationNode addUnusedParamAnnotationList = getAnnotationIfPresent(method.visibleAnnotations, AddUnusedParam.List.class);
211+
if (addUnusedParamAnnotationList != null) {
212+
for (AnnotationNode annotation : ((List<AnnotationNode>) addUnusedParamAnnotationList.values.get(1))) {
213+
try {
214+
addedParameters.add(AddedParameter.parse(annotation));
215+
} catch (RefImpl.RefAnnotationGivenNoArguments e) {
216+
methodExceptions.addException(e);
217+
}
218+
}
219+
} else {
220+
AnnotationNode addUnusedParamAnnotation = getAnnotationIfPresent(method.invisibleAnnotations, AddUnusedParam.class);
221+
if (addUnusedParamAnnotation != null) {
222+
try {
223+
addedParameters.add(AddedParameter.parse(addUnusedParamAnnotation));
224+
} catch (RefImpl.RefAnnotationGivenNoArguments e) {
225+
methodExceptions.addException(e);
226+
}
215227
}
216228
}
217229
return addedParameters;

src/main/java/io/github/notstirred/dasm/annotation/parse/AddedParameter.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111
@Value
1212
public class AddedParameter {
1313
Type type;
14+
int index;
1415

1516
public static AddedParameter parse(AnnotationNode annotation) throws RefImpl.RefAnnotationGivenNoArguments {
1617
Map<String, Object> values = AnnotationUtil.getAnnotationValues(annotation, AddUnusedParam.class);
1718

1819
Type type = RefImpl.parseRefAnnotation("type", values);
19-
return new AddedParameter(type);
20+
int index = (int) values.get("index");
21+
22+
return new AddedParameter(type, index);
2023
}
2124
}

src/main/java/io/github/notstirred/dasm/transformer/ParameterAdder.java

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,42 +3,95 @@
33
import io.github.notstirred.dasm.annotation.parse.AddedParameter;
44
import org.objectweb.asm.Label;
55
import org.objectweb.asm.MethodVisitor;
6+
import org.objectweb.asm.Type;
7+
import org.objectweb.asm.tree.MethodNode;
68

9+
import java.util.Arrays;
710
import java.util.List;
811

12+
import static org.objectweb.asm.Opcodes.ACC_STATIC;
913
import static org.objectweb.asm.Opcodes.ASM9;
1014

1115
public class ParameterAdder extends MethodVisitor {
12-
private final List<AddedParameter> addedParameters;
16+
private final int addedParameterTypeSize; // longs and doubles take up two spaces in the LVT for no reason whatsoever.
17+
private final boolean isStatic;
18+
private final int idxOfFirstLocal;
19+
private final int[] parameterIndicesLUT;
1320

14-
private int localVariables;
21+
private final String newMethodDescriptor;
22+
private final Type owner;
1523

1624
private int maxStack;
1725
private int maxLocals;
1826

1927
private Label startLabel;
2028
private Label endLabel;
2129

22-
public ParameterAdder(MethodVisitor visitor, List<AddedParameter> addedParameters) {
23-
super(ASM9, visitor);
30+
public ParameterAdder(MethodNode node, Type owner, String originalMethodDescriptor, String newMethodDescriptor, List<AddedParameter> addedParameters) {
31+
super(ASM9, node);
32+
this.owner = owner;
33+
this.isStatic = (node.access & ACC_STATIC) != 0;
34+
int lvtSize = Arrays.stream(Type.getArgumentTypes(originalMethodDescriptor)).mapToInt(Type::getSize).sum() + (this.isStatic ? 0 : 1);
35+
this.idxOfFirstLocal = lvtSize;
36+
this.newMethodDescriptor = newMethodDescriptor;
37+
this.parameterIndicesLUT = new int[lvtSize];
2438

25-
this.addedParameters = addedParameters;
39+
for (int i = 0; i < parameterIndicesLUT.length; i++) {
40+
parameterIndicesLUT[i] = i;
41+
}
42+
int addedParamSize = 0;
43+
for (AddedParameter addedParameter : addedParameters) {
44+
addedParamSize += addedParameter.type().getSize();
45+
for (int i = addedParameter.index() + (this.isStatic ? 0 : 1); i < parameterIndicesLUT.length; i += addedParameter.type().getSize()) {
46+
parameterIndicesLUT[i]++;
47+
}
48+
}
49+
this.addedParameterTypeSize = addedParamSize;
2650
}
2751

2852
@Override
29-
public void visitLabel(Label label) {
30-
super.visitLabel(label);
53+
public void visitCode() {
54+
super.visitCode();
55+
56+
Type[] methodArgs = Type.getArgumentTypes(this.newMethodDescriptor);
3157

32-
if (this.startLabel == null) {
33-
this.startLabel = label;
58+
endLabel = new Label();
59+
startLabel = new Label();
60+
super.visitLabel(startLabel);
61+
62+
int localIdx = 0;
63+
if (!this.isStatic) {
64+
super.visitLocalVariable("this", this.owner.getDescriptor(), null, startLabel, endLabel, localIdx++);
65+
}
66+
for (Type methodArg : methodArgs) {
67+
super.visitLocalVariable("param" + localIdx, methodArg.getDescriptor(), null, startLabel, endLabel, localIdx);
68+
localIdx += methodArg.getSize();
69+
}
70+
}
71+
72+
@Override
73+
public void visitVarInsn(int opcode, int varIndex) {
74+
if (varIndex >= idxOfFirstLocal) { // a local variable
75+
super.visitVarInsn(opcode, varIndex + this.addedParameterTypeSize);
76+
} else { // a parameter
77+
super.visitVarInsn(opcode, this.parameterIndicesLUT[varIndex]);
78+
}
79+
}
80+
81+
@Override
82+
public void visitIincInsn(int varIndex, int increment) {
83+
if (varIndex >= idxOfFirstLocal) { // a local variable
84+
super.visitIincInsn(varIndex + this.addedParameterTypeSize, increment);
85+
} else { // a parameter
86+
super.visitIincInsn(this.parameterIndicesLUT[varIndex], increment);
3487
}
35-
this.endLabel = label;
3688
}
3789

3890
@Override
3991
public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) {
40-
super.visitLocalVariable(name, descriptor, signature, start, end, index);
41-
this.localVariables++;
92+
if (index >= this.idxOfFirstLocal) {
93+
super.visitLocalVariable(name, descriptor, signature, start, end, index + this.addedParameterTypeSize);
94+
}
4295
}
4396

4497
@Override
@@ -49,10 +102,10 @@ public void visitMaxs(int maxStack, int maxLocals) {
49102

50103
@Override
51104
public void visitEnd() {
52-
for (AddedParameter addedParameter : this.addedParameters) {
53-
super.visitLocalVariable("foo", addedParameter.type().getDescriptor(), null, this.startLabel, this.endLabel, this.localVariables);
54-
}
55-
super.visitMaxs(this.maxStack, this.maxLocals + this.addedParameters.size());
105+
super.visitMaxs(this.maxStack, this.maxLocals + this.addedParameterTypeSize);
106+
107+
super.visitLabel(this.endLabel);
108+
56109
super.visitEnd();
57110
}
58111
}

src/main/java/io/github/notstirred/dasm/transformer/Transformer.java

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,13 @@ public void transform(ClassNode targetClass, ClassTransform transform) throws No
101101

102102
String redirectedDescriptor = applyTransformsToMethodDescriptor(descriptor, redirects, Collections.emptyList());
103103

104+
MethodVisitor visitor = super.visitMethod(access, dstName, redirectedDescriptor, signature, exceptions);
105+
assert visitor instanceof MethodNode; // We assume that we receive a MethodNode (ClassNode always returns one)
104106
return dasmTransformingVisitor(
105-
super.visitMethod(access, dstName, redirectedDescriptor, signature, exceptions),
107+
redirectedDescriptor,
108+
Type.getObjectType(targetClass.name),
109+
descriptor,
110+
(MethodNode) visitor,
106111
redirects,
107112
mappingsProvider,
108113
Collections.emptyList()
@@ -173,7 +178,7 @@ private MethodNode cloneAndApplyRedirects(ClassNode srcClass, ClassNode targetCl
173178
// FIXME: transform exceptions
174179
MethodNode dstMethodNode = new MethodNode(srcMethodNode.access, dstMethodName, dstMethodDescriptor, null, srcMethodNode.exceptions.toArray(new String[0]));
175180

176-
srcMethodNode.accept(dasmTransformingVisitor(dstMethodNode, redirects, mappingsProvider, addedParameters));
181+
srcMethodNode.accept(dasmTransformingVisitor(dstMethodDescriptor, Type.getObjectType(targetClass.name), srcMethodNode.desc, dstMethodNode, redirects, mappingsProvider, addedParameters));
177182

178183
dstMethodNode.name = dstMethodName;
179184

@@ -184,10 +189,11 @@ private MethodNode cloneAndApplyRedirects(ClassNode srcClass, ClassNode targetCl
184189
/**
185190
* Apply all dasm transforms to a method body
186191
*/
187-
private static MethodVisitor dasmTransformingVisitor(MethodVisitor visitor, TransformRedirects redirects, MappingsProvider mappingsProvider,
192+
private static MethodVisitor dasmTransformingVisitor(String newMethodDescriptor, Type owner, String originalMethodDesc, MethodNode node,
193+
TransformRedirects redirects, MappingsProvider mappingsProvider,
188194
List<AddedParameter> addedParameters) {
189195
// FIXME: line numbers
190-
visitor = new ParameterAdder(visitor, addedParameters);
196+
MethodVisitor visitor = new ParameterAdder(node, owner, originalMethodDesc, newMethodDescriptor, addedParameters);
191197
visitor = new Interfacicitifier(visitor, redirects);
192198
visitor = new MethodRemapper(visitor, new TypeRemapper(redirects.typeRedirects(), false, mappingsProvider));
193199
visitor = new RedirectVisitor(visitor, redirects, mappingsProvider);
@@ -201,24 +207,36 @@ private static String applyTransformsToMethodDescriptor(String methodDescriptor,
201207
Type returnType = Type.getReturnType(methodDescriptor);
202208

203209
for (int i = 0; i < parameterTypes.length; i++) {
204-
if (parameterTypes[i].getSort() == Type.OBJECT) {
205-
TypeAndIsInterface type = redirects.typeRedirects().get(parameterTypes[i]);
206-
parameterTypes[i] = type != null ? type.type() : parameterTypes[i];
207-
}
210+
parameterTypes[i] = redirectType(parameterTypes[i], redirects);
208211
}
209212

210-
if (returnType.getSort() == Type.OBJECT) {
211-
returnType = redirects.typeRedirects().getOrDefault(returnType, new TypeAndIsInterface(returnType, false)).type();
212-
}
213+
returnType = redirectType(returnType, redirects);
213214

214215
List<Type> parameterTypeList = new ArrayList<>(Arrays.asList(parameterTypes));
215-
for (AddedParameter addedParameter : addedParameters) {
216-
parameterTypeList.add(addedParameter.type());
217-
}
216+
217+
// add parameters such that indices are always with respect to the original method signature and do not change as parameters are added.
218+
int[] addedParameterCount = new int[]{0}; // java is stupid
219+
addedParameters.stream().sorted(Comparator.comparingInt(AddedParameter::index))
220+
.forEachOrdered(addedParameter -> {
221+
parameterTypeList.add(addedParameter.index() + addedParameterCount[0], addedParameter.type());
222+
addedParameterCount[0]++;
223+
});
218224

219225
return Type.getMethodDescriptor(returnType, parameterTypeList.toArray(new Type[0]));
220226
}
221227

228+
private static Type redirectType(Type parameterType, TransformRedirects redirects) {
229+
if (parameterType.getSort() == Type.ARRAY) {
230+
TypeAndIsInterface type = redirects.typeRedirects().get(parameterType.getElementType());
231+
String arrayPart = String.join("", Collections.nCopies(parameterType.getDimensions(), "["));
232+
parameterType = type != null ? Type.getType(arrayPart + type.type()) : parameterType;
233+
} else {
234+
TypeAndIsInterface type = redirects.typeRedirects().get(parameterType);
235+
parameterType = type != null ? type.type() : parameterType;
236+
}
237+
return parameterType;
238+
}
239+
222240
private void cloneAndApplyLambdaRedirects(ClassNode srcClass, ClassNode targetClass, MethodNode method, TransformRedirects redirects,
223241
boolean debugLogging) throws SrcMethodNotFound {
224242
Map<Handle, String> lambdaRedirects = new HashMap<>();
@@ -294,7 +312,7 @@ private void applyRedirects(ClassNode srcClass, ClassMethod srcMethod, Transform
294312

295313
MethodNode dstMethodNode = new MethodNode(originalMethod.access, originalMethod.name, dstMethodDescriptor, null, originalMethod.exceptions.toArray(new String[0]));
296314
originalMethod.accept(
297-
dasmTransformingVisitor(dstMethodNode, redirects, mappingsProvider, addedParameters)
315+
dasmTransformingVisitor(dstMethodDescriptor, Type.getObjectType(srcClass.name), originalMethod.desc, dstMethodNode, redirects, mappingsProvider, addedParameters)
298316
);
299317
srcClass.methods.remove(originalMethod);
300318
srcClass.methods.add(dstMethodNode);

src/main/java/io/github/notstirred/dasm/transformer/TypeRemapper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@
22

33
import io.github.notstirred.dasm.api.provider.MappingsProvider;
44
import io.github.notstirred.dasm.transformer.data.TypeAndIsInterface;
5+
import io.github.notstirred.dasm.util.RemapperWithPrimitives;
56
import org.apache.logging.log4j.LogManager;
67
import org.apache.logging.log4j.Logger;
78
import org.objectweb.asm.Type;
8-
import org.objectweb.asm.commons.Remapper;
99

1010
import java.util.HashMap;
1111
import java.util.Map;
1212

13-
public class TypeRemapper extends Remapper {
13+
public class TypeRemapper extends RemapperWithPrimitives {
1414
public static final String SKIP_TYPE_REDIRECT_PREFIX = "dasm_redirect[";
1515

1616
private static final Logger LOGGER = LogManager.getLogger();

0 commit comments

Comments
 (0)