Skip to content

Commit

Permalink
Move logic from Paper
Browse files Browse the repository at this point in the history
  • Loading branch information
Machine-Maker committed Feb 13, 2024
1 parent a3a86d3 commit 360695e
Show file tree
Hide file tree
Showing 36 changed files with 1,499 additions and 471 deletions.
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ allprojects {
val asm = "org.ow2.asm:asm:9.6"
api(asm)
testImplementation(asm)
val asmCommons = "org.ow2.asm:asm-commons:9.6"
api(asmCommons)
testImplementation(asmCommons)
}

val checker = "org.checkerframework:checker-qual:3.42.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package io.papermc.reflectionrewriter;

import io.papermc.asm.InvokeStaticRewrite;
import io.papermc.asm.MethodMatcher;
import io.papermc.asm.RewriteRule;
import io.papermc.asm.rules.RewriteRule;
import java.lang.invoke.ConstantBootstraps;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.List;
import java.util.Set;

public final class BaseReflectionRules {
private final String proxy;
Expand Down Expand Up @@ -54,91 +55,53 @@ public List<RewriteRule> rules() {
}

private RewriteRule createClassRule() {
final MethodMatcher getNamedMatcher = MethodMatcher.builder()
.match(Set.of("getField", "getDeclaredField"), "(Ljava/lang/String;)Ljava/lang/reflect/Field;")
.match(Set.of("getMethod", "getDeclaredMethod"), "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;")
.build();
return RewriteRule.create(InvokeStaticRewrite.forOwner(
"java/lang/Class",
(context, opcode, owner, name, descriptor, isInterface) -> {
if (name.equals("forName")) {
return this.redirectToProxy(name, descriptor);
} else if (getNamedMatcher.matches(name, descriptor)) {
return this.redirectToProxy(name, InvokeStaticRewrite.insertFirstParam("java/lang/Class", descriptor));
}
return null;
}
));
return RewriteRule.forOwner(Class.class, rf -> {
rf.plainStaticRewrite(this.proxy, b -> b
.match("getField", "getDeclaredField").desc("(Ljava/lang/String;)Ljava/lang/reflect/Field;")
.match("getMethod", "getDeclaredMethod").desc("(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;")
);
});
}

private RewriteRule createMethodHandlesLookupRule() {
final MethodMatcher matcher = MethodMatcher.builder()
.match(Set.of("findStatic", "findVirtual"), "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;")
.match("findClass", "(Ljava/lang/String;)Ljava/lang/Class;")
.match("findSpecial", "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;")
.match(Set.of("findGetter", "findSetter", "findStaticGetter", "findStaticSetter"), "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;")
.match(Set.of("findVarHandle", "findStaticVarHandle"), "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;")
.match("bind", "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;")
.build();
return RewriteRule.create(InvokeStaticRewrite.forOwner(
"java/lang/invoke/MethodHandles$Lookup",
(context, opcode, owner, name, descriptor, isInterface) -> {
if (matcher.matches(name, descriptor)) {
return this.redirectToProxy(name, InvokeStaticRewrite.insertFirstParam("java/lang/invoke/MethodHandles$Lookup", descriptor));
}
return null;
}
));
return RewriteRule.forOwner(MethodHandles.Lookup.class, rf -> {
rf.plainStaticRewrite(this.proxy, b -> b
.match("findStatic", "findVirtual").desc("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;")
.match("findClass").desc("(Ljava/lang/String;)Ljava/lang/Class;")
.match("findSpecial").desc("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;")
.match("findGetter", "findSetter", "findStaticGetter", "findStaticSetter").desc("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/MethodHandle;")
.match("findVarHandle", "findStaticVarHandle").desc("(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;")
.match("bind").desc("(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;")
);
});
}

private RewriteRule createLamdaMetafactoryRule() {
final MethodMatcher matcher = MethodMatcher.builder()
.match("metafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;")
.match("altMetafactory", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;")
.build();
return RewriteRule.create(InvokeStaticRewrite.forOwner(
"java/lang/invoke/LambdaMetafactory",
(context, opcode, owner, name, descriptor, isInterface) -> {
if (matcher.matches(name, descriptor)) {
return this.redirectToProxy(name, descriptor);
}
return null;
}
));
return RewriteRule.forOwner(LambdaMetafactory.class, rf -> {
rf.plainStaticRewrite(this.proxy, b -> b
.match("metafactory").desc("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;")
.match("altMetafactory").desc("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;")
);
});
}

private RewriteRule createConstantBootstrapsRule() {
final MethodMatcher matcher = MethodMatcher.builder()
.match("getStaticFinal", Set.of(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
))
.match(Set.of("fieldVarHandle", "staticFieldVarHandle"), "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;")
.build();
return RewriteRule.create(InvokeStaticRewrite.forOwner(
"java/lang/invoke/ConstantBootstraps",
(context, opcode, owner, name, descriptor, isInterface) -> {
if (matcher.matches(name, descriptor)) {
return this.redirectToProxy(name, descriptor);
}
return null;
}
));
return RewriteRule.forOwner(ConstantBootstraps.class, rf -> {
rf.plainStaticRewrite(this.proxy, b -> b
.match("getStaticFinal").desc(
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Object;"
)
.match("fieldVarHandle", "staticFieldVarHandle").desc("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/invoke/VarHandle;")
);
});
}

private RewriteRule createMethodTypeRule() {
return RewriteRule.create(InvokeStaticRewrite.forOwner(
"java/lang/invoke/MethodType",
(context, opcode, owner, name, descriptor, isInterface) -> {
if (name.equals("fromMethodDescriptorString") && descriptor.equals("(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;")) {
return this.redirectToProxy(name, descriptor);
}
return null;
}
));
}

private InvokeStaticRewrite.Rewrite redirectToProxy(final String name, final String descriptor) {
return InvokeStaticRewrite.staticRedirect(this.proxy, name, descriptor);
return RewriteRule.forOwner(MethodType.class, rf -> {
rf.plainStaticRewrite(this.proxy, b -> b
.match("fromMethodDescriptorString").desc("(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/invoke/MethodType;")
);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,21 @@
import io.papermc.asm.ClassInfo;
import io.papermc.asm.ClassInfoProvider;
import io.papermc.asm.ClassProcessingContext;
import io.papermc.asm.InvokeStaticRewrite;
import io.papermc.asm.RewriteRule;
import io.papermc.asm.rules.MethodRewriteRule;
import io.papermc.asm.rules.RewriteRule;
import io.papermc.asm.rules.builder.matcher.MethodMatcher;
import java.lang.constant.MethodTypeDesc;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.framework.qual.DefaultQualifier;
import org.objectweb.asm.Opcodes;

import static io.papermc.asm.util.DescriptorUtils.fromOwner;
import static io.papermc.asm.util.OpcodeUtils.isStatic;
import static io.papermc.asm.util.OpcodeUtils.staticOp;

@DefaultQualifier(NonNull.class)
public final class DefineClassRule implements InvokeStaticRewrite {
public final class DefineClassRule implements MethodRewriteRule {
private static final Set<String> CLASS_LOADER_DESCS = Set.of(
"([BII)Ljava/lang/Class;",
"(Ljava/lang/String;[BII)Ljava/lang/Class;",
Expand All @@ -36,37 +41,39 @@ private DefineClassRule(final String proxyClassName, final boolean assumeClassLo
// extend (S)CL. However since the MethodHandles.Lookup portion always needs to run, the actual benefit would
// be beyond minute (if not actually worse).
@Override
public @Nullable Rewrite rewrite(
final ClassProcessingContext context,
final int opcode,
final String owner,
final String name,
final String descriptor,
final boolean isInterface
public @Nullable Rewrite rewrite(final ClassProcessingContext context,
final boolean invokeDynamic,
final int opcode,
final String owner,
final String name,
MethodTypeDesc descriptor,
final boolean isInterface
) {
if (!name.equals("defineClass") || opcode == Opcodes.INVOKESTATIC || opcode == Opcodes.H_INVOKESTATIC) {
if (!name.equals("defineClass") || isStatic(opcode, invokeDynamic)) {
return null;
}
if (owner.equals("java/lang/invoke/MethodHandles$Lookup") && descriptor.equals("([B)Ljava/lang/Class;")) {
return InvokeStaticRewrite.staticRedirect(this.proxy, name, InvokeStaticRewrite.insertFirstParam("java/lang/invoke/MethodHandles$Lookup", descriptor));
if (owner.equals("java/lang/invoke/MethodHandles$Lookup") && descriptor.descriptorString().equals("([B)Ljava/lang/Class;")) {
descriptor = descriptor.insertParameterTypes(0, fromOwner("java/lang/invoke/MethodHandles$Lookup"));
new RewriteSingle(staticOp(invokeDynamic), this.proxy, name, descriptor, false);
}
final @Nullable String superName = context.processingClassSuperClassName();
if (superName != null) {
if (CLASS_LOADER_DESCS.contains(descriptor)
if (CLASS_LOADER_DESCS.contains(descriptor.descriptorString())
&& this.isClassLoader(context.classInfoProvider(), superName)
&& (owner.equals(context.processingClassName()) || this.isClassLoader(context.classInfoProvider(), owner))) {
return this.classLoaderRewrite(name, descriptor);
} else if (SECURE_CLASS_LOADER_DESCS.contains(descriptor)
return this.classLoaderRewrite(invokeDynamic, name, descriptor);
} else if (SECURE_CLASS_LOADER_DESCS.contains(descriptor.descriptorString())
&& this.isSecureClassLoader(context.classInfoProvider(), superName)
&& (owner.equals(context.processingClassName()) || this.isSecureClassLoader(context.classInfoProvider(), owner))) {
return this.classLoaderRewrite(name, descriptor);
return this.classLoaderRewrite(invokeDynamic, name, descriptor);
}
}
return null;
}

private Rewrite classLoaderRewrite(final String name, final String descriptor) {
return InvokeStaticRewrite.staticRedirect(this.proxy, name, InvokeStaticRewrite.insertFirstParam("java/lang/Object", descriptor));
private MethodRewriteRule.Rewrite classLoaderRewrite(final boolean invokeDynamic, final String name, MethodTypeDesc descriptor) {
descriptor = descriptor.insertParameterTypes(0, fromOwner("java/lang/Object"));
return new RewriteSingle(staticOp(invokeDynamic), this.proxy, name, descriptor, false);
}

private boolean isSecureClassLoader(final ClassInfoProvider classInfoProvider, final String className) {
Expand Down Expand Up @@ -96,7 +103,7 @@ public static RewriteRule create(final String proxyClassName) {
* @return new rule
*/
public static RewriteRule create(final String proxyClassName, final boolean assumeClassLoader) {
return RewriteRule.create(new DefineClassRule(proxyClassName, assumeClassLoader));
return new DefineClassRule(proxyClassName, assumeClassLoader);
}

private static boolean is(final ClassInfoProvider classInfoProvider, final String className, final String checkForName, final boolean assumeClassLoader) {
Expand All @@ -114,4 +121,34 @@ private static boolean is(final ClassInfoProvider classInfoProvider, final Strin
}
return assumeClassLoader;
}

@Override
public boolean matchesOwner(final ClassProcessingContext context, final String owner) {
final @Nullable String superName = context.processingClassSuperClassName();
if (superName != null) {
final boolean isClassLoader = this.isClassLoader(context.classInfoProvider(), superName) && (owner.equals(context.processingClassName()) || this.isClassLoader(context.classInfoProvider(), owner));
final boolean isSecureClassLoader = this.isSecureClassLoader(context.classInfoProvider(), superName) && (owner.equals(context.processingClassName()) || this.isSecureClassLoader(context.classInfoProvider(), owner));
return isClassLoader || isSecureClassLoader;
}
return false;
}

@Override
public Set<Class<?>> owners() {
throw new UnsupportedOperationException("dynamic owners");
}

@Override
public MethodMatcher methodMatcher() {
return MethodMatcher.builder()
.match("defineClass").desc(
"([BII)Ljava/lang/Class;",
"(Ljava/lang/String;[BII)Ljava/lang/Class;",
"(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;",
"(Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class;",
"(Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class;",
"(Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class;"
)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@

import io.papermc.asm.ClassInfo;
import io.papermc.asm.ClassInfoProvider;
import io.papermc.asm.InvokeStaticRewrite;
import io.papermc.asm.RewriteRule;
import io.papermc.asm.ClassProcessingContext;
import io.papermc.asm.rules.RewriteRule;
import java.lang.invoke.ConstantBootstraps;
import java.util.function.Predicate;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
Expand Down Expand Up @@ -50,20 +52,23 @@ public static RewriteRule create(
final String proxyClassName,
final Predicate<String> ownerPredicate
) {
final InvokeStaticRewrite rewrite = InvokeStaticRewrite.forOwner(
"java/lang/invoke/ConstantBootstraps",
(context, opcode, owner, name, descriptor, isInterface) -> {
if (name.equals("enumConstant") && descriptor.equals("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Enum;")) {
return InvokeStaticRewrite.staticRedirect(proxyClassName, name, descriptor);
}
return null;
final RewriteRule rewrite = RewriteRule.forOwner(ConstantBootstraps.class, rf -> {
rf.plainStaticRewrite(proxyClassName, b -> b
.match("enumConstant").desc("(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Enum;")
);
});
final RewriteRule enumRule = new RewriteRule() {
@Override
public ClassVisitor createVisitor(final int api, final ClassVisitor parent, final ClassProcessingContext context) {
return new ClassVisitor(api, parent) {
@Override
public MethodVisitor visitMethod(final int access, final String name, final String descriptor, final String signature, final String[] exceptions) {
return new EnumMethodVisitor(this.api, super.visitMethod(access, name, descriptor, signature, exceptions), proxyClassName, context.classInfoProvider(), ownerPredicate);
}
};
}
);
return RewriteRule.create((api, parent, context) -> rewrite.createVisitor(
api,
new EnumMethodVisitor(api, parent, proxyClassName, context.classInfoProvider(), ownerPredicate),
context
));
};
return RewriteRule.chain(rewrite, enumRule);
}

public static RewriteRule minecraft(final String proxyClassName) {
Expand Down
25 changes: 0 additions & 25 deletions src/main/java/io/papermc/asm/BasicRewriteRule.java

This file was deleted.

Loading

0 comments on commit 360695e

Please sign in to comment.