mirror of
https://github.com/ViaVersion/ViaProxy.git
synced 2024-11-26 12:35:11 +01:00
Implemented JavaDowngrader
This commit is contained in:
parent
f2aeb9f485
commit
0a7f14ae9f
12
build.gradle
12
build.gradle
@ -71,7 +71,7 @@ repositories {
|
||||
dependencies {
|
||||
compileOnly sourceSets.java17compat.output
|
||||
|
||||
include "com.viaversion:viaversion:4.7.0-1.20-pre1-SNAPSHOT"
|
||||
include "com.viaversion:viaversion:4.7.0-1.20-pre2-SNAPSHOT"
|
||||
include("com.viaversion:viabackwards-common:4.7.0-1.20-pre1-SNAPSHOT") {
|
||||
exclude group: "com.viaversion", module: "viaversion"
|
||||
exclude group: "io.netty", module: "netty-all"
|
||||
@ -101,9 +101,9 @@ dependencies {
|
||||
include "org.apache.logging.log4j:log4j-core:2.20.0"
|
||||
include "org.apache.logging.log4j:log4j-slf4j-impl:2.20.0"
|
||||
include "com/mojang:authlib:3.16.29"
|
||||
include "net.lenni0451.classtransform:mixinstranslator:1.9.0-SNAPSHOT"
|
||||
include "net.lenni0451.classtransform:mixinsdummy:1.9.0-SNAPSHOT"
|
||||
include "net.lenni0451.classtransform:additionalclassprovider:1.9.0-SNAPSHOT"
|
||||
include "net.lenni0451.classtransform:mixinstranslator:1.9.1"
|
||||
include "net.lenni0451.classtransform:mixinsdummy:1.9.1"
|
||||
include "net.lenni0451.classtransform:additionalclassprovider:1.9.1"
|
||||
include "net.lenni0451:Reflect:1.1.0"
|
||||
include "net.lenni0451:LambdaEvents:2.0.3"
|
||||
include "net.raphimc.netminecraft:all:2.3.3"
|
||||
@ -111,6 +111,10 @@ dependencies {
|
||||
exclude group: "com.google.code.gson", module: "gson"
|
||||
exclude group: "org.slf4j", module: "slf4j-api"
|
||||
}
|
||||
include("net.raphimc.javadowngrader:core:1.0.0-SNAPSHOT") {
|
||||
exclude group: "org.slf4j", module: "slf4j-api"
|
||||
exclude group: "org.ow2.asm", module: "asm-commons"
|
||||
}
|
||||
include "com.vdurmont:semver4j:3.1.0"
|
||||
include("org.cloudburstmc.netty:netty-transport-raknet:1.0.0.CR1-SNAPSHOT") {
|
||||
exclude group: "io.netty", module: "netty-common"
|
||||
|
@ -20,49 +20,17 @@ package net.raphimc.viaproxy.injection;
|
||||
import net.lenni0451.classtransform.TransformerManager;
|
||||
import net.lenni0451.classtransform.transformer.IBytecodeTransformer;
|
||||
import net.lenni0451.classtransform.utils.ASMUtils;
|
||||
import net.lenni0451.classtransform.utils.tree.BasicClassProvider;
|
||||
import net.lenni0451.classtransform.utils.tree.ClassTree;
|
||||
import net.lenni0451.classtransform.utils.tree.IClassProvider;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.tree.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import net.raphimc.javadowngrader.JavaDowngrader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Java17ToJava8 implements IBytecodeTransformer {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(Java17ToJava8.class);
|
||||
|
||||
private static final boolean DEBUG_DUMP = Boolean.getBoolean("j17to8.dump");
|
||||
|
||||
private static final char STACK_ARG_CONSTANT = '\u0001';
|
||||
private static final char BSM_ARG_CONSTANT = '\u0002';
|
||||
|
||||
private static final String TRANSFERTO_DESC = "(Ljava/io/InputStream;Ljava/io/OutputStream;)J";
|
||||
|
||||
private static final String EQUALS_DESC = "(Ljava/lang/Object;)Z";
|
||||
private static final String HASHCODE_DESC = "()I";
|
||||
private static final String TOSTRING_DESC = "()Ljava/lang/String;";
|
||||
private static final Map<String, String> PRIMITIVE_WRAPPERS = new HashMap<>();
|
||||
|
||||
static {
|
||||
PRIMITIVE_WRAPPERS.put("V", Type.getInternalName(Void.class));
|
||||
PRIMITIVE_WRAPPERS.put("Z", Type.getInternalName(Boolean.class));
|
||||
PRIMITIVE_WRAPPERS.put("B", Type.getInternalName(Byte.class));
|
||||
PRIMITIVE_WRAPPERS.put("S", Type.getInternalName(Short.class));
|
||||
PRIMITIVE_WRAPPERS.put("C", Type.getInternalName(Character.class));
|
||||
PRIMITIVE_WRAPPERS.put("I", Type.getInternalName(Integer.class));
|
||||
PRIMITIVE_WRAPPERS.put("F", Type.getInternalName(Float.class));
|
||||
PRIMITIVE_WRAPPERS.put("J", Type.getInternalName(Long.class));
|
||||
PRIMITIVE_WRAPPERS.put("D", Type.getInternalName(Double.class));
|
||||
}
|
||||
|
||||
private final ClassTree classTree;
|
||||
private final IClassProvider classProvider;
|
||||
private final int nativeClassVersion;
|
||||
@ -83,14 +51,6 @@ public class Java17ToJava8 implements IBytecodeTransformer {
|
||||
this(transformerManager.getClassTree(), transformerManager.getClassProvider());
|
||||
}
|
||||
|
||||
public Java17ToJava8(final IClassProvider classProvider) {
|
||||
this(new ClassTree(), classProvider);
|
||||
}
|
||||
|
||||
public Java17ToJava8(final ClassLoader loader) {
|
||||
this(new BasicClassProvider(loader));
|
||||
}
|
||||
|
||||
public Java17ToJava8 addWhitelistedPackage(final String packageName) {
|
||||
this.whitelistedPackages.add(packageName);
|
||||
return this;
|
||||
@ -98,11 +58,15 @@ public class Java17ToJava8 implements IBytecodeTransformer {
|
||||
|
||||
@Override
|
||||
public byte[] transform(final String className, final byte[] bytecode, final boolean calculateStackMapFrames) {
|
||||
if (!whitelistedPackages.isEmpty()) {
|
||||
if (ByteBuffer.wrap(bytecode, 4, 4).getInt() <= this.nativeClassVersion) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!this.whitelistedPackages.isEmpty()) {
|
||||
int dotIndex = className.lastIndexOf('.');
|
||||
if (dotIndex == -1 && !whitelistedPackages.contains("")) return null;
|
||||
if (dotIndex == -1 && !this.whitelistedPackages.contains("")) return null;
|
||||
String pkg = className.substring(0, dotIndex);
|
||||
while (!whitelistedPackages.contains(pkg)) {
|
||||
while (!this.whitelistedPackages.contains(pkg)) {
|
||||
dotIndex = pkg.lastIndexOf('.');
|
||||
if (dotIndex == -1) return null;
|
||||
pkg = pkg.substring(0, dotIndex);
|
||||
@ -110,738 +74,13 @@ public class Java17ToJava8 implements IBytecodeTransformer {
|
||||
}
|
||||
|
||||
final ClassNode classNode = ASMUtils.fromBytes(bytecode);
|
||||
if (classNode.version <= this.nativeClassVersion) return null;
|
||||
|
||||
classNode.version = Opcodes.V1_8;
|
||||
this.makePackagePrivate(classNode);
|
||||
this.convertStringConcatFactory(classNode);
|
||||
this.convertListMethods(classNode);
|
||||
this.convertSetMethods(classNode);
|
||||
this.convertMapMethods(classNode);
|
||||
this.convertStreamMethods(classNode);
|
||||
this.convertMiscMethods(classNode);
|
||||
this.convertRecords(classNode);
|
||||
JavaDowngrader.downgrade(classNode, this.nativeClassVersion);
|
||||
|
||||
if (calculateStackMapFrames) {
|
||||
final byte[] result = ASMUtils.toBytes(classNode, classTree, classProvider);
|
||||
if (DEBUG_DUMP) {
|
||||
try {
|
||||
final File file = new File("vp_17to8_dump", classNode.name + ".class");
|
||||
file.getParentFile().mkdirs();
|
||||
Files.write(file.toPath(), result);
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error("Failed to dump class {}", className, e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return ASMUtils.toBytes(classNode, this.classTree, this.classProvider);
|
||||
} else {
|
||||
return ASMUtils.toStacklessBytes(classNode);
|
||||
}
|
||||
}
|
||||
|
||||
private void makePackagePrivate(final ClassNode classNode) {
|
||||
if (classNode.nestHostClass == null) return;
|
||||
for (final MethodNode methodNode : classNode.methods) {
|
||||
methodNode.access &= ~Opcodes.ACC_PRIVATE;
|
||||
}
|
||||
for (final FieldNode fieldNode : classNode.fields) {
|
||||
fieldNode.access &= ~Opcodes.ACC_PRIVATE;
|
||||
}
|
||||
}
|
||||
|
||||
private void convertStringConcatFactory(final ClassNode node) {
|
||||
for (MethodNode method : node.methods) {
|
||||
for (AbstractInsnNode instruction : method.instructions.toArray()) {
|
||||
if (instruction.getOpcode() == Opcodes.INVOKEDYNAMIC) {
|
||||
InvokeDynamicInsnNode insn = (InvokeDynamicInsnNode) instruction;
|
||||
if (insn.bsm.getOwner().equals("java/lang/invoke/StringConcatFactory") && insn.bsm.getName().equals("makeConcatWithConstants")) {
|
||||
String pattern = (String) insn.bsmArgs[0];
|
||||
Type[] stackArgs = Type.getArgumentTypes(insn.desc);
|
||||
Object[] bsmArgs = Arrays.copyOfRange(insn.bsmArgs, 1, insn.bsmArgs.length);
|
||||
int stackArgsCount = count(pattern, STACK_ARG_CONSTANT);
|
||||
int bsmArgsCount = count(pattern, BSM_ARG_CONSTANT);
|
||||
|
||||
if (stackArgs.length != stackArgsCount) throw new IllegalStateException("Stack args count does not match");
|
||||
if (bsmArgs.length != bsmArgsCount) throw new IllegalStateException("BSM args count does not match");
|
||||
|
||||
int freeVarIndex = ASMUtils.getFreeVarIndex(method);
|
||||
int[] stackIndices = new int[stackArgsCount];
|
||||
for (int i = 0; i < stackArgs.length; i++) {
|
||||
stackIndices[i] = freeVarIndex;
|
||||
freeVarIndex += stackArgs[i].getSize();
|
||||
}
|
||||
for (int i = stackIndices.length - 1; i >= 0; i--) {
|
||||
method.instructions.insertBefore(insn, new VarInsnNode(stackArgs[i].getOpcode(Opcodes.ISTORE), stackIndices[i]));
|
||||
}
|
||||
|
||||
InsnList converted = convertStringConcatFactory(pattern, stackArgs, stackIndices, bsmArgs);
|
||||
method.instructions.insertBefore(insn, converted);
|
||||
method.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertListMethods(final ClassNode node) {
|
||||
for (MethodNode method : node.methods) {
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKESTATIC) {
|
||||
final MethodInsnNode min = (MethodInsnNode) insn;
|
||||
if (!min.owner.equals("java/util/List")) continue;
|
||||
|
||||
final InsnList list = new InsnList();
|
||||
|
||||
if (min.name.equals("of")) {
|
||||
final Type[] args = Type.getArgumentTypes(min.desc);
|
||||
if (args.length != 1 || args[0].getSort() != Type.ARRAY) {
|
||||
int freeVarIndex = ASMUtils.getFreeVarIndex(method);
|
||||
|
||||
int argCount = args.length;
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
|
||||
list.add(new VarInsnNode(Opcodes.ASTORE, freeVarIndex));
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z"));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/util/Collections",
|
||||
"reverse",
|
||||
"(Ljava/util/List;)V"
|
||||
));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableList", "(Ljava/util/List;)Ljava/util/List;"));
|
||||
}
|
||||
} else if (min.name.equals("copyOf")) {
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
|
||||
list.add(new InsnNode(Opcodes.DUP_X1));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "(Ljava/util/Collection;)V"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableList", "(Ljava/util/List;)Ljava/util/List;"));
|
||||
}
|
||||
|
||||
if (list.size() != 0) {
|
||||
method.instructions.insertBefore(insn, list);
|
||||
method.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertSetMethods(final ClassNode node) {
|
||||
for (MethodNode method : node.methods) {
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKESTATIC) {
|
||||
final MethodInsnNode min = (MethodInsnNode) insn;
|
||||
if (!min.owner.equals("java/util/Set")) continue;
|
||||
|
||||
final InsnList list = new InsnList();
|
||||
|
||||
if (min.name.equals("of")) {
|
||||
final Type[] args = Type.getArgumentTypes(min.desc);
|
||||
if (args.length != 1 || args[0].getSort() != Type.ARRAY) {
|
||||
int freeVarIndex = ASMUtils.getFreeVarIndex(method);
|
||||
|
||||
int argCount = args.length;
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashSet"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashSet", "<init>", "()V"));
|
||||
list.add(new VarInsnNode(Opcodes.ASTORE, freeVarIndex));
|
||||
for (int i = 0; i < argCount; i++) {
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z"));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableSet", "(Ljava/util/Set;)Ljava/util/Set;"));
|
||||
}
|
||||
} else if (min.name.equals("copyOf")) {
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashSet"));
|
||||
list.add(new InsnNode(Opcodes.DUP_X1));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashSet", "<init>", "(Ljava/util/Collection;)V"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableSet", "(Ljava/util/Set;)Ljava/util/Set;"));
|
||||
}
|
||||
|
||||
if (list.size() != 0) {
|
||||
method.instructions.insertBefore(insn, list);
|
||||
method.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertMapMethods(final ClassNode node) {
|
||||
for (MethodNode method : node.methods) {
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKESTATIC) {
|
||||
final MethodInsnNode min = (MethodInsnNode) insn;
|
||||
if (!min.owner.equals("java/util/Map")) continue;
|
||||
|
||||
final InsnList list = new InsnList();
|
||||
|
||||
if (min.name.equals("of")) {
|
||||
final Type[] args = Type.getArgumentTypes(min.desc);
|
||||
if (args.length != 1 || args[0].getSort() != Type.ARRAY) {
|
||||
int freeVarIndex = ASMUtils.getFreeVarIndex(method);
|
||||
|
||||
int argCount = args.length;
|
||||
if (argCount % 2 != 0) {
|
||||
throw new RuntimeException("Map.of() requires an even number of arguments");
|
||||
}
|
||||
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashMap"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"));
|
||||
list.add(new VarInsnNode(Opcodes.ASTORE, freeVarIndex));
|
||||
for (int i = 0; i < argCount / 2; i++) {
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex));
|
||||
list.add(new InsnNode(Opcodes.DUP_X2));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, freeVarIndex));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableMap", "(Ljava/util/Map;)Ljava/util/Map;"));
|
||||
}
|
||||
} else if (min.name.equals("copyOf")) {
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashMap"));
|
||||
list.add(new InsnNode(Opcodes.DUP_X1));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashMap", "<init>", "(Ljava/util/Map;)V"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Collections", "unmodifiableMap", "(Ljava/util/Map;)Ljava/util/Map;"));
|
||||
}
|
||||
|
||||
if (list.size() != 0) {
|
||||
method.instructions.insertBefore(insn, list);
|
||||
method.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertStreamMethods(final ClassNode node) {
|
||||
for (MethodNode method : node.methods) {
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKEINTERFACE) {
|
||||
final MethodInsnNode min = (MethodInsnNode) insn;
|
||||
if (!min.owner.equals("java/util/stream/Stream")) continue;
|
||||
|
||||
final InsnList list = new InsnList();
|
||||
|
||||
if (min.name.equals("toList")) {
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/util/stream/Collectors",
|
||||
"toList",
|
||||
"()Ljava/util/stream/Collector;"
|
||||
));
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKEINTERFACE,
|
||||
"java/util/stream/Stream",
|
||||
"collect",
|
||||
"(Ljava/util/stream/Collector;)Ljava/lang/Object;"
|
||||
));
|
||||
list.add(new TypeInsnNode(Opcodes.CHECKCAST, "java/util/List"));
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/util/Collections",
|
||||
"unmodifiableList",
|
||||
"(Ljava/util/List;)Ljava/util/List;")
|
||||
);
|
||||
}
|
||||
|
||||
if (list.size() != 0) {
|
||||
method.instructions.insertBefore(insn, list);
|
||||
method.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void convertMiscMethods(final ClassNode node) {
|
||||
boolean needsTransferTo = false;
|
||||
String transferToName;
|
||||
{
|
||||
int i = 0;
|
||||
do {
|
||||
transferToName = "transferTo$" + i;
|
||||
} while (ASMUtils.getMethod(node, transferToName, TRANSFERTO_DESC) != null);
|
||||
}
|
||||
|
||||
for (MethodNode method : node.methods) {
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn instanceof MethodInsnNode) {
|
||||
final MethodInsnNode min = (MethodInsnNode) insn;
|
||||
final InsnList list = new InsnList();
|
||||
|
||||
if (min.owner.equals("java/lang/String")) {
|
||||
if (min.name.equals("isBlank") && min.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/String", "trim", "()Ljava/lang/String;"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z"));
|
||||
}
|
||||
} else if (min.owner.equals("java/io/InputStream")) {
|
||||
if (min.name.equals("readAllBytes") && min.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
needsTransferTo = true;
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/io/ByteArrayOutputStream"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKESPECIAL,
|
||||
"java/io/ByteArrayOutputStream",
|
||||
"<init>",
|
||||
"()V"
|
||||
));
|
||||
list.add(new InsnNode(Opcodes.DUP_X1));
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKESTATIC,
|
||||
node.name,
|
||||
transferToName,
|
||||
TRANSFERTO_DESC
|
||||
));
|
||||
list.add(new InsnNode(Opcodes.POP2));
|
||||
list.add(new MethodInsnNode(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
"java/io/ByteArrayOutputStream",
|
||||
"toByteArray",
|
||||
"()[B"
|
||||
));
|
||||
} else if (min.name.equals("transferTo") && min.getOpcode() == Opcodes.INVOKEVIRTUAL) {
|
||||
needsTransferTo = true;
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC,
|
||||
node.name,
|
||||
transferToName,
|
||||
TRANSFERTO_DESC
|
||||
));
|
||||
}
|
||||
} else if (min.owner.equals("java/nio/file/FileSystems")) {
|
||||
if (min.name.equals("newFileSystem") && min.desc.equals("(Ljava/nio/file/Path;Ljava/util/Map;Ljava/lang/ClassLoader;)Ljava/nio/file/FileSystem;")) {
|
||||
list.add(new InsnNode(Opcodes.DUP2_X1));
|
||||
list.add(new InsnNode(Opcodes.POP2));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/nio/file/Path", "toUri", "()Ljava/net/URI;"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/net/URI", "toString", "()Ljava/lang/String;"));
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuilder"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V"));
|
||||
list.add(new LdcInsnNode("jar:"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"));
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/net/URI"));
|
||||
list.add(new InsnNode(Opcodes.DUP_X1));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/net/URI", "<init>", "(Ljava/lang/String;)V"));
|
||||
list.add(new InsnNode(Opcodes.DUP_X2));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/nio/file/FileSystems", "newFileSystem", "(Ljava/net/URI;Ljava/util/Map;Ljava/lang/ClassLoader;)Ljava/nio/file/FileSystem;"));
|
||||
}
|
||||
} else if (min.owner.equals("java/util/Objects")) {
|
||||
if (min.name.equals("requireNonNullElse")) {
|
||||
LabelNode elseJump = new LabelNode();
|
||||
LabelNode endJump = new LabelNode();
|
||||
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new JumpInsnNode(Opcodes.IFNULL, elseJump));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
list.add(new JumpInsnNode(Opcodes.GOTO, endJump));
|
||||
list.add(elseJump);
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
list.add(new LdcInsnNode("defaultObj"));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Objects", "requireNonNull", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;"));
|
||||
list.add(endJump);
|
||||
}
|
||||
} else if (min.owner.equals("java/nio/file/Files")) {
|
||||
if (min.name.equals("readString")) {
|
||||
if (min.desc.equals("(Ljava/nio/file/Path;)Ljava/lang/String;")) {
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/nio/file/Files", "readAllBytes", "(Ljava/nio/file/Path;)[B"));
|
||||
list.add(new FieldInsnNode(Opcodes.GETSTATIC, "java/nio/charset/StandardCharsets", "UTF_8", "Ljava/nio/charset/Charset;"));
|
||||
} else if (min.desc.equals("(Ljava/nio/file/Path;Ljava/nio/charset/Charset;)Ljava/lang/String;")) {
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/nio/file/Files", "readAllBytes", "(Ljava/nio/file/Path;)[B"));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
}
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/lang/String"));
|
||||
list.add(new InsnNode(Opcodes.DUP_X2));
|
||||
list.add(new InsnNode(Opcodes.DUP_X2));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/String", "<init>", "([BLjava/nio/charset/Charset;)V"));
|
||||
}
|
||||
} else if (min.owner.equals("java/util/regex/Matcher")) {
|
||||
if (min.name.equals("appendReplacement") && min.desc.equals("(Ljava/lang/StringBuilder;Ljava/lang/String;)Ljava/util/regex/Matcher;")) {
|
||||
int stringBufferIndex = ASMUtils.getFreeVarIndex(method);
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuffer"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V"));
|
||||
list.add(new VarInsnNode(Opcodes.ASTORE, stringBufferIndex));
|
||||
|
||||
list.add(new InsnNode(Opcodes.DUP2_X1));
|
||||
list.add(new InsnNode(Opcodes.POP2));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, stringBufferIndex));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, min.owner, min.name, "(Ljava/lang/StringBuffer;Ljava/lang/String;)Ljava/util/regex/Matcher;"));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, stringBufferIndex));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;"));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
} else if (min.name.equals("appendTail") && min.desc.equals("(Ljava/lang/StringBuilder;)Ljava/lang/StringBuilder;")) {
|
||||
int stringBufferIndex = ASMUtils.getFreeVarIndex(method);
|
||||
list.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuffer"));
|
||||
list.add(new InsnNode(Opcodes.DUP));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "()V"));
|
||||
list.add(new VarInsnNode(Opcodes.ASTORE, stringBufferIndex));
|
||||
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, stringBufferIndex));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, min.owner, min.name, "(Ljava/lang/StringBuffer;)Ljava/lang/StringBuffer;"));
|
||||
list.add(new InsnNode(Opcodes.SWAP));
|
||||
list.add(new VarInsnNode(Opcodes.ALOAD, stringBufferIndex));
|
||||
list.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;"));
|
||||
list.add(new InsnNode(Opcodes.POP));
|
||||
}
|
||||
}
|
||||
|
||||
if (list.size() != 0) {
|
||||
method.instructions.insertBefore(insn, list);
|
||||
method.instructions.remove(insn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsTransferTo) {
|
||||
// I compiled this by hand btw
|
||||
final MethodVisitor transferTo = node.visitMethod(
|
||||
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
|
||||
transferToName, TRANSFERTO_DESC, null, new String[] {"java/io/IOException"}
|
||||
);
|
||||
transferTo.visitCode();
|
||||
|
||||
// Objects.requireNonNull(out, "out");
|
||||
transferTo.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
transferTo.visitLdcInsn("out");
|
||||
transferTo.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/util/Objects",
|
||||
"requireNonNull",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;",
|
||||
false
|
||||
);
|
||||
transferTo.visitInsn(Opcodes.POP);
|
||||
|
||||
// long transferred = 0;
|
||||
transferTo.visitInsn(Opcodes.LCONST_0);
|
||||
transferTo.visitVarInsn(Opcodes.LSTORE, 2);
|
||||
|
||||
// byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
|
||||
transferTo.visitIntInsn(Opcodes.SIPUSH, 8192);
|
||||
transferTo.visitIntInsn(Opcodes.NEWARRAY, Opcodes.T_BYTE);
|
||||
transferTo.visitVarInsn(Opcodes.ASTORE, 4);
|
||||
|
||||
// while ((read = this.read(buffer, 0, DEFAULT_BUFFER_SIZE)) >= 0) {
|
||||
final Label whileStart = new Label();
|
||||
final Label whileEnd = new Label();
|
||||
transferTo.visitLabel(whileStart);
|
||||
transferTo.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
transferTo.visitVarInsn(Opcodes.ALOAD, 4);
|
||||
transferTo.visitInsn(Opcodes.ICONST_0);
|
||||
transferTo.visitIntInsn(Opcodes.SIPUSH, 8192);
|
||||
transferTo.visitMethodInsn(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
"java/io/InputStream",
|
||||
"read",
|
||||
"([BII)I",
|
||||
false
|
||||
);
|
||||
transferTo.visitInsn(Opcodes.DUP);
|
||||
transferTo.visitVarInsn(Opcodes.ISTORE, 5);
|
||||
transferTo.visitJumpInsn(Opcodes.IFLT, whileEnd);
|
||||
|
||||
// out.write(buffer, 0, read);
|
||||
transferTo.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
transferTo.visitVarInsn(Opcodes.ALOAD, 4);
|
||||
transferTo.visitInsn(Opcodes.ICONST_0);
|
||||
transferTo.visitVarInsn(Opcodes.ILOAD, 5);
|
||||
transferTo.visitMethodInsn(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
"java/io/OutputStream",
|
||||
"write",
|
||||
"([BII)V",
|
||||
false
|
||||
);
|
||||
|
||||
// transferred += read;
|
||||
transferTo.visitVarInsn(Opcodes.LLOAD, 2);
|
||||
transferTo.visitVarInsn(Opcodes.ILOAD, 5);
|
||||
transferTo.visitInsn(Opcodes.I2L);
|
||||
transferTo.visitInsn(Opcodes.LADD);
|
||||
transferTo.visitVarInsn(Opcodes.LSTORE, 2);
|
||||
|
||||
// }
|
||||
transferTo.visitJumpInsn(Opcodes.GOTO, whileStart);
|
||||
transferTo.visitLabel(whileEnd);
|
||||
|
||||
// return transferred;
|
||||
transferTo.visitVarInsn(Opcodes.LLOAD, 2);
|
||||
transferTo.visitInsn(Opcodes.LRETURN);
|
||||
|
||||
transferTo.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private void convertRecords(final ClassNode node) {
|
||||
if (!node.superName.equals("java/lang/Record")) return;
|
||||
|
||||
node.access &= ~Opcodes.ACC_RECORD;
|
||||
node.superName = "java/lang/Object";
|
||||
|
||||
final List<MethodNode> constructors = ASMUtils.getMethodsFromCombi(node, "<init>");
|
||||
for (MethodNode method : constructors) {
|
||||
for (AbstractInsnNode insn : method.instructions.toArray()) {
|
||||
if (insn.getOpcode() == Opcodes.INVOKESPECIAL) {
|
||||
MethodInsnNode min = (MethodInsnNode) insn;
|
||||
if (min.owner.equals("java/lang/Record")) {
|
||||
min.owner = "java/lang/Object";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.methods.remove(ASMUtils.getMethod(node, "equals", EQUALS_DESC));
|
||||
final MethodVisitor equals = node.visitMethod(Opcodes.ACC_PUBLIC, "equals", EQUALS_DESC, null, null);
|
||||
{
|
||||
equals.visitCode();
|
||||
|
||||
equals.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
equals.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
final Label notSameLabel = new Label();
|
||||
equals.visitJumpInsn(Opcodes.IF_ACMPNE, notSameLabel);
|
||||
equals.visitInsn(Opcodes.ICONST_1);
|
||||
equals.visitInsn(Opcodes.IRETURN);
|
||||
equals.visitLabel(notSameLabel);
|
||||
|
||||
// Original uses Class.isInstance, but I think instanceof is more fitting here
|
||||
equals.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
equals.visitTypeInsn(Opcodes.INSTANCEOF, node.name);
|
||||
final Label notIsInstanceLabel = new Label();
|
||||
equals.visitJumpInsn(Opcodes.IFNE, notIsInstanceLabel);
|
||||
equals.visitInsn(Opcodes.ICONST_0);
|
||||
equals.visitInsn(Opcodes.IRETURN);
|
||||
equals.visitLabel(notIsInstanceLabel);
|
||||
|
||||
equals.visitVarInsn(Opcodes.ALOAD, 1);
|
||||
equals.visitTypeInsn(Opcodes.CHECKCAST, node.name);
|
||||
equals.visitVarInsn(Opcodes.ASTORE, 2);
|
||||
|
||||
final Label notEqualLabel = new Label();
|
||||
for (final RecordComponentNode component : node.recordComponents) {
|
||||
equals.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
equals.visitFieldInsn(Opcodes.GETFIELD, node.name, component.name, component.descriptor);
|
||||
equals.visitVarInsn(Opcodes.ALOAD, 2);
|
||||
equals.visitFieldInsn(Opcodes.GETFIELD, node.name, component.name, component.descriptor);
|
||||
if (Type.getType(component.descriptor).getSort() >= Type.ARRAY) { // ARRAY or OBJECT
|
||||
equals.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
Type.getInternalName(Objects.class),
|
||||
"equals",
|
||||
"(Ljava/lang/Object;Ljava/lang/Object;)Z",
|
||||
false
|
||||
);
|
||||
equals.visitJumpInsn(Opcodes.IFEQ, notEqualLabel);
|
||||
continue;
|
||||
} else if ("BSCIZ".contains(component.descriptor)) {
|
||||
equals.visitJumpInsn(Opcodes.IF_ICMPNE, notEqualLabel);
|
||||
continue;
|
||||
} else if (component.descriptor.equals("F")) {
|
||||
equals.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
Type.getInternalName(Float.class),
|
||||
"equals",
|
||||
"(FF)Z",
|
||||
false
|
||||
);
|
||||
} else if (component.descriptor.equals("D")) {
|
||||
equals.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
Type.getInternalName(Double.class),
|
||||
"equals",
|
||||
"(DD)Z",
|
||||
false
|
||||
);
|
||||
} else if (component.descriptor.equals("J")) {
|
||||
equals.visitInsn(Opcodes.LCMP);
|
||||
} else {
|
||||
throw new AssertionError("Unknown descriptor " + component.descriptor);
|
||||
}
|
||||
equals.visitJumpInsn(Opcodes.IFNE, notEqualLabel);
|
||||
}
|
||||
equals.visitInsn(Opcodes.ICONST_1);
|
||||
equals.visitInsn(Opcodes.IRETURN);
|
||||
equals.visitLabel(notEqualLabel);
|
||||
equals.visitInsn(Opcodes.ICONST_0);
|
||||
equals.visitInsn(Opcodes.IRETURN);
|
||||
|
||||
equals.visitEnd();
|
||||
}
|
||||
|
||||
node.methods.remove(ASMUtils.getMethod(node, "hashCode", HASHCODE_DESC));
|
||||
final MethodVisitor hashCode = node.visitMethod(Opcodes.ACC_PUBLIC, "hashCode", HASHCODE_DESC, null, null);
|
||||
{
|
||||
hashCode.visitCode();
|
||||
|
||||
hashCode.visitInsn(Opcodes.ICONST_0);
|
||||
for (final RecordComponentNode component : node.recordComponents) {
|
||||
hashCode.visitIntInsn(Opcodes.BIPUSH, 31);
|
||||
hashCode.visitInsn(Opcodes.IMUL);
|
||||
hashCode.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
hashCode.visitFieldInsn(Opcodes.GETFIELD, node.name, component.name, component.descriptor);
|
||||
final String owner = PRIMITIVE_WRAPPERS.get(component.descriptor);
|
||||
hashCode.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
owner != null ? owner : "java/util/Objects",
|
||||
"hashCode",
|
||||
"(" + (owner != null ? component.descriptor : "Ljava/lang/Object;") + ")I",
|
||||
false
|
||||
);
|
||||
hashCode.visitInsn(Opcodes.IADD);
|
||||
}
|
||||
hashCode.visitInsn(Opcodes.IRETURN);
|
||||
|
||||
hashCode.visitEnd();
|
||||
}
|
||||
|
||||
node.methods.remove(ASMUtils.getMethod(node, "toString", TOSTRING_DESC));
|
||||
final MethodVisitor toString = node.visitMethod(Opcodes.ACC_PUBLIC, "toString", TOSTRING_DESC, null, null);
|
||||
{
|
||||
toString.visitCode();
|
||||
|
||||
final StringBuilder formatString = new StringBuilder("%s[");
|
||||
for (int i = 0; i < node.recordComponents.size(); i++) {
|
||||
formatString.append(node.recordComponents.get(i).name).append("=%s");
|
||||
if (i != node.recordComponents.size() - 1) {
|
||||
formatString.append(", ");
|
||||
}
|
||||
}
|
||||
formatString.append(']');
|
||||
|
||||
toString.visitLdcInsn(formatString.toString());
|
||||
toString.visitIntInsn(Opcodes.SIPUSH, node.recordComponents.size() + 1);
|
||||
toString.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object");
|
||||
toString.visitInsn(Opcodes.DUP);
|
||||
toString.visitInsn(Opcodes.ICONST_0);
|
||||
toString.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
toString.visitMethodInsn(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Object",
|
||||
"getClass",
|
||||
"()Ljava/lang/Class;",
|
||||
false
|
||||
);
|
||||
toString.visitMethodInsn(
|
||||
Opcodes.INVOKEVIRTUAL,
|
||||
"java/lang/Class",
|
||||
"getSimpleName",
|
||||
"()Ljava/lang/String;",
|
||||
false
|
||||
);
|
||||
toString.visitInsn(Opcodes.AASTORE);
|
||||
int i = 1;
|
||||
for (final RecordComponentNode component : node.recordComponents) {
|
||||
toString.visitInsn(Opcodes.DUP);
|
||||
toString.visitIntInsn(Opcodes.SIPUSH, i);
|
||||
toString.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
toString.visitFieldInsn(Opcodes.GETFIELD, node.name, component.name, component.descriptor);
|
||||
final String owner = PRIMITIVE_WRAPPERS.get(component.descriptor);
|
||||
toString.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
owner != null ? owner : "java/util/Objects",
|
||||
"toString",
|
||||
"(" + (owner != null ? component.descriptor : "Ljava/lang/Object;") + ")Ljava/lang/String;",
|
||||
false
|
||||
);
|
||||
toString.visitInsn(Opcodes.AASTORE);
|
||||
i++;
|
||||
}
|
||||
toString.visitMethodInsn(
|
||||
Opcodes.INVOKESTATIC,
|
||||
"java/lang/String",
|
||||
"format",
|
||||
"(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;",
|
||||
false
|
||||
);
|
||||
toString.visitInsn(Opcodes.ARETURN);
|
||||
|
||||
toString.visitEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private int count(final String s, final char search) {
|
||||
char[] chars = s.toCharArray();
|
||||
int count = 0;
|
||||
for (char c : chars) {
|
||||
if (c == search) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private InsnList convertStringConcatFactory(final String pattern, final Type[] stackArgs, final int[] stackIndices, final Object[] bsmArgs) {
|
||||
InsnList insns = new InsnList();
|
||||
char[] chars = pattern.toCharArray();
|
||||
int stackArgsIndex = 0;
|
||||
int bsmArgsIndex = 0;
|
||||
StringBuilder partBuilder = new StringBuilder();
|
||||
|
||||
insns.add(new TypeInsnNode(Opcodes.NEW, "java/lang/StringBuilder"));
|
||||
insns.add(new InsnNode(Opcodes.DUP));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V"));
|
||||
for (char c : chars) {
|
||||
if (c == STACK_ARG_CONSTANT) {
|
||||
if (partBuilder.length() != 0) {
|
||||
insns.add(new LdcInsnNode(partBuilder.toString()));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
|
||||
partBuilder = new StringBuilder();
|
||||
}
|
||||
|
||||
Type stackArg = stackArgs[stackArgsIndex++];
|
||||
int stackIndex = stackIndices[stackArgsIndex - 1];
|
||||
if (stackArg.getSort() == Type.OBJECT) {
|
||||
insns.add(new VarInsnNode(Opcodes.ALOAD, stackIndex));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"));
|
||||
} else if (stackArg.getSort() == Type.ARRAY) {
|
||||
insns.add(new VarInsnNode(Opcodes.ALOAD, stackIndex));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;"));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
|
||||
} else {
|
||||
insns.add(new VarInsnNode(stackArg.getOpcode(Opcodes.ILOAD), stackIndex));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(" + stackArg.getDescriptor() + ")Ljava/lang/StringBuilder;"));
|
||||
}
|
||||
} else if (c == BSM_ARG_CONSTANT) {
|
||||
insns.add(new LdcInsnNode(bsmArgs[bsmArgsIndex++]));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;"));
|
||||
} else {
|
||||
partBuilder.append(c);
|
||||
}
|
||||
}
|
||||
if (partBuilder.length() != 0) {
|
||||
insns.add(new LdcInsnNode(partBuilder.toString()));
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;"));
|
||||
}
|
||||
insns.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;"));
|
||||
return insns;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ public abstract class MixinProtocolVersion {
|
||||
remaps.put("1.16.4/5", new Pair<>("1.16.4-1.16.5", null));
|
||||
remaps.put("1.18/1.18.1", new Pair<>("1.18-1.18.1", null));
|
||||
remaps.put("1.19.1/2", new Pair<>("1.19.1-1.19.2", null));
|
||||
remaps.put("1.20", new Pair<>("1.20-pre1", null));
|
||||
remaps.put("1.20", new Pair<>("1.20-pre2", null));
|
||||
}
|
||||
|
||||
@Redirect(method = "<clinit>", at = @At(value = "INVOKE", target = "Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;register(ILjava/lang/String;)Lcom/viaversion/viaversion/api/protocol/version/ProtocolVersion;"))
|
||||
|
Loading…
Reference in New Issue
Block a user