Skip to content

Commit e3c2607

Browse files
committed
finish adding invoke dynamic tests
1 parent f107b5b commit e3c2607

File tree

18 files changed

+133
-35
lines changed

18 files changed

+133
-35
lines changed

src/main/java/io/papermc/asm/rules/builder/RuleFactoryImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ public void changeParamToSuper(final ClassDesc legacyParamType, final ClassDesc
4444
}
4545

4646
@Override
47-
public void changeParamFuzzy(final ClassDesc modernParamType, final Method staticHandler, final Consumer<? super TargetedMethodMatcher.Builder> builderConsumer) {
48-
this.addRule(new StaticRewrites.FuzzyParam(this.owners, modernParamType, build(builderConsumer, MethodMatcher::targeted), isStatic(staticHandler)));
47+
public void changeParamFuzzy(final ClassDesc newParamType, final Method staticHandler, final Consumer<? super TargetedMethodMatcher.Builder> builderConsumer) {
48+
this.addRule(new StaticRewrites.FuzzyParam(this.owners, newParamType, build(builderConsumer, MethodMatcher::targeted), isStatic(staticHandler)));
4949
}
5050

5151
@Override
52-
public void changeParamDirect(final ClassDesc existingParam, final Method staticHandler, final Consumer<? super TargetedMethodMatcher.Builder> builderConsumer) {
53-
this.addRule(new StaticRewrites.DirectParam(this.owners, existingParam, build(builderConsumer, MethodMatcher::targeted), isStatic(staticHandler)));
52+
public void changeParamDirect(final ClassDesc newParamType, final Method staticHandler, final Consumer<? super TargetedMethodMatcher.Builder> builderConsumer) {
53+
this.addRule(new StaticRewrites.DirectParam(this.owners, newParamType, build(builderConsumer, MethodMatcher::targeted), isStatic(staticHandler)));
5454
}
5555

5656
@Override

src/main/java/io/papermc/asm/rules/classes/ClassToInterfaceRule.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public Set<ClassDesc> owners() {
4343
}
4444

4545
@Override
46-
public Rewrite<?> rewrite(final ClassProcessingContext context, final boolean isInvokeDynamic, final int opcode, final ClassDesc owner, final String name, final MethodTypeDesc descriptor, final boolean isInterface) {
46+
public @Nullable Rewrite<?> rewrite(final ClassProcessingContext context, final boolean isInvokeDynamic, final int opcode, final ClassDesc owner, final String name, final MethodTypeDesc descriptor, final boolean isInterface) {
4747
if (isStatic(opcode, isInvokeDynamic)) {
4848
return new RewriteSingle(opcode, owner, name, descriptor, true, isInvokeDynamic);
4949
}
@@ -55,7 +55,7 @@ public Rewrite<?> rewrite(final ClassProcessingContext context, final boolean is
5555
} else if (ClassToInterfaceRule.this.redirectExtension != null && opcode == Opcodes.INVOKESPECIAL && StaticRewrite.CONSTRUCTOR_METHOD_NAME.equals(name)) {
5656
return new RewriteSingle(opcode, ClassToInterfaceRule.this.redirectExtension, name, descriptor, isInterface, isInvokeDynamic);
5757
} else {
58-
throw new IllegalStateException("Unexpected opcode: " + opcode + ". There should only be invokevirtual or h_invokevirtual opcodes here.");
58+
return null; // just don't rewrite if nothing matches (maybe they already compiled against the interface)
5959
}
6060
return new RewriteSingle(newOpcode, owner, name, descriptor, true, isInvokeDynamic);
6161
}

src/main/java/io/papermc/asm/rules/method/MethodRewriteRule.java

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
import java.lang.constant.MethodTypeDesc;
88
import java.util.LinkedHashMap;
99
import java.util.Map;
10+
import java.util.function.Consumer;
1011
import org.checkerframework.checker.nullness.qual.Nullable;
1112
import org.objectweb.asm.ClassVisitor;
1213
import org.objectweb.asm.Handle;
1314
import org.objectweb.asm.MethodVisitor;
1415
import org.objectweb.asm.Opcodes;
15-
import org.objectweb.asm.Type;
1616
import org.objectweb.asm.commons.GeneratorAdapter;
1717
import org.objectweb.asm.tree.MethodNode;
1818

@@ -127,6 +127,10 @@ interface Rewrite<D extends GeneratedMethodHolder.CallData> {
127127

128128
Rewrite<D> withNamePrefix(String prefix);
129129

130+
default Rewrite<D> withHandleExtras(final Consumer<Object[]> extras) {
131+
return this;
132+
}
133+
130134
@Nullable MethodGenerator createMethodGenerator();
131135
}
132136

@@ -150,10 +154,19 @@ interface MethodGenerator {
150154
* @param isInvokeDynamic if the replaced method is an invokedynamic
151155
* @param generatorInfo info for generating the method (optional)
152156
*/
153-
record RewriteSingle(int opcode, ClassDesc owner, String name, MethodTypeDesc descriptor, boolean isInterface, boolean isInvokeDynamic, @Nullable GeneratorInfo<GeneratedMethodHolder.MethodCallData> generatorInfo) implements Rewrite<GeneratedMethodHolder.MethodCallData> {
157+
record RewriteSingle(
158+
int opcode,
159+
ClassDesc owner,
160+
String name,
161+
MethodTypeDesc descriptor,
162+
boolean isInterface,
163+
boolean isInvokeDynamic,
164+
@Nullable GeneratorInfo<GeneratedMethodHolder.MethodCallData> generatorInfo,
165+
@Nullable Consumer<Object[]> handleExtras
166+
) implements Rewrite<GeneratedMethodHolder.MethodCallData> {
154167

155168
public RewriteSingle(final int opcode, final ClassDesc owner, final String name, final MethodTypeDesc descriptor, final boolean isInterface, final boolean isInvokeDynamic) {
156-
this(opcode, owner, name, descriptor, isInterface, isInvokeDynamic, null);
169+
this(opcode, owner, name, descriptor, isInterface, isInvokeDynamic, null, null);
157170
}
158171

159172
@Override
@@ -164,28 +177,24 @@ public void apply(final MethodVisitor delegate, final MethodNode context) {
164177
@Override
165178
public void applyToBootstrapArguments(final Object[] arguments) {
166179
arguments[BOOTSTRAP_HANDLE_IDX] = new Handle(this.opcode(), toOwner(this.owner()), this.name(), this.descriptor().descriptorString(), this.isInterface());
167-
// so here's what's happening...
168-
// the dynamicMethodType needs to match the parameters being passed in at
169-
// runtime. The dynamicReturnType is then updated to match the descriptor
170-
// of the method handle, except it sometimes drop the first parameter ensuring
171-
// the parameter count doesn't change
172-
final Type dynamicMethodType = (Type) arguments[DYNAMIC_TYPE_IDX];
173-
final Type[] argumentTypes = dynamicMethodType.getArgumentTypes();
174-
final MethodTypeDesc newDynamicMethodType = this.descriptor()
175-
.dropParameterTypes(0, this.descriptor().parameterCount() - argumentTypes.length)
176-
// preserve original return type due to boxing/unboxing
177-
.changeReturnType(ClassDesc.ofDescriptor(dynamicMethodType.getReturnType().getDescriptor()));
178-
arguments[DYNAMIC_TYPE_IDX] = Type.getMethodType(newDynamicMethodType.descriptorString());
180+
if (this.handleExtras != null) {
181+
this.handleExtras.accept(arguments);
182+
}
179183
}
180184

181185
@Override
182186
public Rewrite<GeneratedMethodHolder.MethodCallData> withNamePrefix(final String prefix) {
183-
return new RewriteSingle(this.opcode(), this.owner(), prefix + this.name(), this.descriptor(), this.isInterface(), this.isInvokeDynamic(), this.generatorInfo());
187+
return new RewriteSingle(this.opcode(), this.owner(), prefix + this.name(), this.descriptor(), this.isInterface(), this.isInvokeDynamic(), this.generatorInfo(), this.handleExtras());
184188
}
185189

186190
@Override
187191
public Rewrite<GeneratedMethodHolder.MethodCallData> withGeneratorInfo(final GeneratedMethodHolder holder, final GeneratedMethodHolder.MethodCallData original) {
188-
return new RewriteSingle(this.opcode(), this.owner(), this.name(), this.descriptor(), this.isInterface(), this.isInvokeDynamic(), new GeneratorInfo<>(holder, original));
192+
return new RewriteSingle(this.opcode(), this.owner(), this.name(), this.descriptor(), this.isInterface(), this.isInvokeDynamic(), new GeneratorInfo<>(holder, original), this.handleExtras());
193+
}
194+
195+
@Override
196+
public Rewrite<GeneratedMethodHolder.MethodCallData> withHandleExtras(final Consumer<Object[]> extras) {
197+
return new RewriteSingle(this.opcode(), this.owner(), this.name(), this.descriptor(), this.isInterface(), this.isInvokeDynamic(), this.generatorInfo(), extras);
189198
}
190199

191200
@Override

src/main/java/io/papermc/asm/rules/method/StaticRewrite.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public void apply(final MethodVisitor delegate, final MethodNode context) {
149149
@Override
150150
public void applyToBootstrapArguments(final Object[] arguments) {
151151
arguments[BOOTSTRAP_HANDLE_IDX] = new Handle(Opcodes.H_INVOKESTATIC, toOwner(this.staticRedirectOwner()), this.methodName(), this.descriptor().descriptorString(), false);
152+
// TODO not really needed on **every** rewrite, just the fuzzy param ones, but it doesn't seem to break anything since it will always be the same
152153
arguments[DYNAMIC_TYPE_IDX] = Type.getMethodType(this.descriptor().descriptorString());
153154
}
154155

src/main/java/io/papermc/asm/rules/method/StaticRewrites.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import java.lang.constant.MethodTypeDesc;
88
import java.lang.reflect.Method;
99
import java.util.Set;
10+
import org.objectweb.asm.Type;
1011

1112
import static io.papermc.asm.util.DescriptorUtils.desc;
1213
import static io.papermc.asm.util.DescriptorUtils.replaceParameters;
@@ -41,6 +42,20 @@ public MethodTypeDesc transformInvokedDescriptor(final MethodTypeDesc original,
4142
// fuzzy type with the fuzzy param
4243
return replaceParameters(original, isEqual(OBJECT_DESC), this.existingType(), context);
4344
}
45+
46+
@Override
47+
public Rewrite<MethodCallData> createRewrite(final ClassProcessingContext context, final MethodTypeDesc intermediateDescriptor, final MethodCallData originalCallData) {
48+
return Generated.Param.super.createRewrite(context, intermediateDescriptor, originalCallData)
49+
// so here's what's happening...
50+
// the dynamicMethodType needs to match the parameters being passed in at
51+
// runtime. The dynamicReturnType is run through the redirect transformer
52+
// to replace correct parameters .
53+
.withHandleExtras(arguments -> {
54+
final Type dynamicMethodType = (Type) arguments[Rewrite.DYNAMIC_TYPE_IDX];
55+
final MethodTypeDesc newDynamicMethodType = this.transformToRedirectDescriptor(MethodTypeDesc.ofDescriptor(dynamicMethodType.getDescriptor()));
56+
arguments[Rewrite.DYNAMIC_TYPE_IDX] = Type.getMethodType(newDynamicMethodType.descriptorString());
57+
});
58+
}
4459
}
4560

4661
// Uses the methodMatcher against bytecode from plugins. Any matching descriptors will have their name/owner changed to point towards a

src/test/java/io/papermc/asm/rules/classes/ClassToInterfaceTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
import io.papermc.asm.TransformerTest;
66
import io.papermc.asm.checks.TransformerCheck;
77
import io.papermc.asm.rules.RewriteRule;
8+
import org.junit.jupiter.api.Disabled;
89

910
class ClassToInterfaceTest {
1011

12+
@Disabled("needs an update to asm to fix an issue")
1113
@TransformerTest("data.classes.ClassToInterfaceUser")
1214
void testWithNoReplacement(final TransformerCheck check) {
1315
final RewriteRule rule = new ClassToInterfaceRule(
@@ -18,6 +20,7 @@ void testWithNoReplacement(final TransformerCheck check) {
1820
check.run(rule);
1921
}
2022

23+
@Disabled("needs an update to asm to fix an issue")
2124
@TransformerTest("data.classes.ClassToInterfaceRedirectUser")
2225
void testWithReplacement(final TransformerCheck check) {
2326
final RewriteRule rule = new ClassToInterfaceRule(

src/testData/java/data/classes/ClassToInterfaceRedirectUser.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
package data.classes;
22

33
import data.types.classes.SomeAbstractClass;
4+
import java.util.function.Consumer;
5+
import java.util.function.Function;
6+
import java.util.function.Supplier;
47

58
@SuppressWarnings("unused")
69
public final class ClassToInterfaceRedirectUser extends SomeAbstractClass {
710

811
public static void entry() {
9-
SomeAbstractClass someAbstractClass = new ClassToInterfaceRedirectUser();
12+
final SomeAbstractClass someAbstractClass = new ClassToInterfaceRedirectUser();
1013
someAbstractClass.doSomething();
11-
String name = someAbstractClass.getName();
14+
final String name = someAbstractClass.getName();
1215

1316
final String staticString = SomeAbstractClass.getStaticString();
17+
18+
final Consumer<SomeAbstractClass> doSomething = SomeAbstractClass::doSomething;
19+
doSomething.accept(someAbstractClass);
20+
21+
final Supplier<String> getName = someAbstractClass::getName;
22+
final String name2 = getName.get();
23+
24+
final Function<SomeAbstractClass, String> getName2 = SomeAbstractClass::getName;
25+
final String name3 = getName2.apply(someAbstractClass);
26+
27+
final Supplier<String> getStaticString = SomeAbstractClass::getStaticString;
28+
final String staticString2 = getStaticString.get();
1429
}
1530

1631
@Override

src/testData/java/data/classes/ClassToInterfaceUser.java

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

33
import data.types.classes.SomeAbstractClass;
44
import data.types.classes.SomeAbstractClassImpl;
5+
import java.util.function.Consumer;
6+
import java.util.function.Function;
7+
import java.util.function.Supplier;
58

69
@SuppressWarnings("unused")
710
public final class ClassToInterfaceUser {
811

912
public static void entry() {
10-
SomeAbstractClass someAbstractClass = new SomeAbstractClassImpl();
13+
final SomeAbstractClass someAbstractClass = new SomeAbstractClassImpl();
1114
someAbstractClass.doSomething();
12-
String name = someAbstractClass.getName();
15+
final String name = someAbstractClass.getName();
1316

1417
final String staticString = SomeAbstractClass.getStaticString();
18+
19+
final Consumer<SomeAbstractClass> doSomething = SomeAbstractClass::doSomething;
20+
doSomething.accept(someAbstractClass);
21+
22+
final Supplier<String> getName = someAbstractClass::getName;
23+
final String name2 = getName.get();
24+
25+
final Function<SomeAbstractClass, String> getName2 = SomeAbstractClass::getName;
26+
final String name3 = getName2.apply(someAbstractClass);
27+
28+
final Supplier<String> getStaticString = SomeAbstractClass::getStaticString;
29+
final String staticString2 = getStaticString.get();
1530
}
1631
}

src/testData/java/data/methods/inplace/SubTypeReturnUser.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,23 @@
33
import data.methods.Methods;
44
import data.types.hierarchy.Entity;
55
import data.types.hierarchy.Player;
6+
import java.util.function.Function;
7+
import java.util.function.Supplier;
68

79
@SuppressWarnings("unused")
810
public final class SubTypeReturnUser {
911
public static void entry() {
1012
final Methods methods = new Methods();
1113
final Entity player = methods.get();
1214
final Entity player2 = Methods.getStatic();
15+
16+
final Supplier<Entity> get = methods::get;
17+
final Entity player3 = get.get();
18+
19+
final Function<Methods, Entity> get2 = Methods::get;
20+
final Entity player4 = get2.apply(methods);
21+
22+
final Supplier<Entity> getStatic = Methods::getStatic;
23+
final Entity player5 = getStatic.get();
1324
}
1425
}

src/testData/java/data/methods/inplace/SuperTypeParamUser.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import data.methods.Methods;
44
import data.types.hierarchy.Player;
5+
import java.util.function.BiConsumer;
6+
import java.util.function.Consumer;
57

68
@SuppressWarnings("unused")
79
public final class SuperTypeParamUser {
@@ -10,5 +12,14 @@ public static void entry() {
1012
final Methods methods = new Methods();
1113
methods.consume(new Player());
1214
Methods.consumeStatic(new Player());
15+
16+
final Consumer<Player> consume = methods::consume;
17+
consume.accept(new Player());
18+
19+
final BiConsumer<Methods, Player> consume2 = Methods::consume;
20+
consume2.accept(methods, new Player());
21+
22+
final Consumer<Player> consumeStatic = Methods::consumeStatic;
23+
consumeStatic.accept(new Player());
1324
}
1425
}

0 commit comments

Comments
 (0)