Update the constant pool parser for Java 25 (#110)

Co-authored-by: Maya <10861407+serenibyss@users.noreply.github.com>
This commit is contained in:
ah-OOG-ahandMaya authored and GitHub committed 2025-02-04 23:24:33 -06:00
1 parent 5628efe9ab
commit 192b7c86ad
2 files changed
+76 -53

No files matched your search

@@ -25,25 +25,52 @@ package com.gtnewhorizon.gtnhlib.asm;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import org.objectweb.asm.Opcodes;
/**
* Using this class to search for a (single) String reference is > 40 times faster than parsing a class with a
* ClassReader + ClassNode while using way less RAM
*/
public class ClassConstantPoolParser {
private static final int UTF8 = 1;
private static final int INT = 3;
private static final int FLOAT = 4;
private static final int LONG = 5;
private static final int DOUBLE = 6;
private static final int FIELD = 9;
private static final int METH = 10;
private static final int IMETH = 11;
private static final int NAME_TYPE = 12;
private static final int HANDLE = 15;
private static final int INDY = 18;
enum ConstantTypes {
INVALID, // 0 unused, if that ever changes shift this to the next unused index
UTF8,
// 2 unused
INT,
FLOAT,
LONG,
DOUBLE,
CLASS_REF,
STR_REF,
FIELD,
METH_REF,
IMETH_REF,
NAME_TYPE,
// 13,14 unused
METH_HANDLE,
METH_TYPE,
DYNAMIC,
INVOKE_DYNAMIC,
MODULE,
PACKAGE;
// spotless:off
// Indices in this table directly map to the values of these constants - disable spotless to make it easier to
// see this
static final ConstantTypes[] MAP = { INVALID,
UTF8, INVALID, INT, FLOAT,
LONG, DOUBLE, CLASS_REF, STR_REF,
FIELD, METH_REF, IMETH_REF, NAME_TYPE,
INVALID, INVALID, METH_HANDLE, METH_TYPE,
DYNAMIC, INVOKE_DYNAMIC, MODULE, PACKAGE };
//spotless:on
static ConstantTypes toType(byte code) {
var ret = MAP[Byte.toUnsignedInt(code)];
if (ret == INVALID) throw new RuntimeException("Invalid constant type: " + code);
return ret;
}
}
private byte[][] BYTES_TO_SEARCH;
@@ -78,62 +105,58 @@ public class ClassConstantPoolParser {
if (basicClass == null || basicClass.length == 0) {
return false;
}
// checks the class version
if (readShort(6, basicClass) > Opcodes.V1_8) {
final var maxSupported = 69; // Java 25
var major = readShort(6, basicClass);
if (major > maxSupported || (major == maxSupported && readShort(4, basicClass) > 0)) {
return false;
}
// parses the constant pool
final int n = readUnsignedShort(8, basicClass);
// Loop through each entry in the constant pool, getting the size and content before jumping to the next.
// Strings get scanned for the searched constants, and if found result in an early exit.
final int numConstants = readUnsignedShort(8, basicClass);
int index = 10;
for (int i = 1; i < n; ++i) {
final int size;
switch (basicClass[index]) {
case FIELD:
case METH:
case IMETH:
case INT:
case FLOAT:
case NAME_TYPE:
case INDY:
size = 5;
break;
case LONG:
case DOUBLE:
size = 9;
++i;
break;
case UTF8:
for (int i = 1; i < numConstants; ++i) {
int size = -1;
switch (ConstantTypes.toType(basicClass[index])) {
case UTF8 -> {
final int strLen = readUnsignedShort(index + 1, basicClass);
size = 3 + strLen;
for (byte[] bytes : BYTES_TO_SEARCH) {
if (prefixes ? strLen >= bytes.length : strLen == bytes.length) {
boolean found = true;
for (int j = index + 3; j < index + 3 + bytes.length; j++) {
if (basicClass[j] != bytes[j - (index + 3)]) {
found = false;
break;
}
}
if (found) {
return true;
if (prefixes ? strLen < bytes.length : strLen != bytes.length) continue;
boolean found = true;
for (int j = index + 3; j < index + 3 + bytes.length; j++) {
if (basicClass[j] != bytes[j - (index + 3)]) {
found = false;
break;
}
}
if (found) return true;
}
break;
case HANDLE:
size = 4;
break;
default:
size = 3;
break;
}
case INT, FLOAT, FIELD, METH_REF, IMETH_REF, NAME_TYPE, DYNAMIC, INVOKE_DYNAMIC -> size = 5;
case LONG, DOUBLE -> {
size = 9;
++i;
}
case METH_HANDLE -> size = 4;
case CLASS_REF, STR_REF, METH_TYPE, MODULE, PACKAGE -> size = 3;
}
if (size < 0) throw new RuntimeException("Error parsing constant pool!");
index += size;
}
return false;
}
private static short readShort(final int index, byte[] basicClass) {
return (short) (((basicClass[index] & 0xFF) << 8) | (basicClass[index + 1] & 0xFF));
return (short) readUnsignedShort(index, basicClass);
}
private static int readUnsignedShort(final int index, byte[] basicClass) {
@@ -25,7 +25,7 @@ import cpw.mods.fml.relauncher.FMLLaunchHandler;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
@IFMLLoadingPlugin.MCVersion("1.7.10")
@IFMLLoadingPlugin.TransformerExclusions({ "com.gtnewhorizon.gtnhlib.core.transformer",
@IFMLLoadingPlugin.TransformerExclusions({ "com.gtnewhorizon.gtnhlib.asm.", "com.gtnewhorizon.gtnhlib.core.transformer",
"com.gtnewhorizon.gtnhlib.client.renderer.TessellatorManager",
"com.gtnewhorizon.gtnhlib.client.renderer.CapturingTessellator" })
@IFMLLoadingPlugin.SortingIndex(-1000)