mirror of
https://github.com/FalsePattern/FalsePatternLib.git
synced 2026-11-15 21:54:45 +00:00
working on the reflection stuff some more...
This commit is contained in:
11 files changed
+439
-131
No files matched your search
@@ -1,5 +1,6 @@
|
||||
package com.falsepattern.lib.internal;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.val;
|
||||
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
@@ -15,6 +16,8 @@ import java.util.Map;
|
||||
@Name(Tags.MODID)
|
||||
@SortingIndex(500)
|
||||
public class CoreLoadingPlugin implements IFMLLoadingPlugin {
|
||||
@Getter
|
||||
private static boolean obfuscated;
|
||||
static {
|
||||
try {
|
||||
Class.forName("thermos.Thermos");
|
||||
@@ -46,6 +49,7 @@ public class CoreLoadingPlugin implements IFMLLoadingPlugin {
|
||||
|
||||
@Override
|
||||
public void injectData(Map<String, Object> data) {
|
||||
obfuscated = (Boolean) data.get("runtimeDeobfuscationEnabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
package com.falsepattern.lib.reflection;
|
||||
|
||||
import com.falsepattern.lib.internal.CoreLoadingPlugin;
|
||||
import com.falsepattern.lib.internal.FalsePatternLib;
|
||||
import com.falsepattern.lib.reflection.storage.Lookup;
|
||||
import com.falsepattern.lib.reflection.types.MappingType;
|
||||
import com.falsepattern.lib.reflection.types.NameType;
|
||||
import com.falsepattern.lib.reflection.types.UniversalClass;
|
||||
import com.falsepattern.lib.reflection.types.UniversalField;
|
||||
import com.falsepattern.lib.reflection.types.UniversalMethod;
|
||||
import com.falsepattern.lib.util.ResourceUtil;
|
||||
import lombok.NonNull;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.val;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class MappingManager {
|
||||
@@ -37,31 +41,75 @@ public class MappingManager {
|
||||
val fieldMappings = ResourceUtil.getResourceStringFromJar("/fields.csv", FalsePatternLib.class).split("\n");
|
||||
for (int i = 1; i < fieldMappings.length; i++) {
|
||||
val line = fieldMappings[i].split(",");
|
||||
val field = new UniversalField(line, stringPool);
|
||||
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
|
||||
clazz.addField(field);
|
||||
new UniversalField(clazz, line, stringPool);
|
||||
}
|
||||
}
|
||||
{
|
||||
val methodMappings = ResourceUtil.getResourceStringFromJar("/methods.csv", FalsePatternLib.class).split("\n");
|
||||
for (int i = 1; i < methodMappings.length; i++) {
|
||||
val line = methodMappings[i].split(",");
|
||||
val field = new UniversalMethod(line, stringPool);
|
||||
val clazz = internalLookup.get(MappingType.Notch, line[0].substring(0, line[0].lastIndexOf('/')));
|
||||
clazz.addMethod(field);
|
||||
new UniversalMethod(clazz, line, stringPool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static UniversalClass classForName(@NonNull NameType nameType, @NonNull MappingType mappingType, String className) {
|
||||
public static UniversalClass classForName(NameType nameType, MappingType mappingType, String className) throws ClassNotFoundException {
|
||||
initialize();
|
||||
switch (nameType) {
|
||||
case Internal:
|
||||
return internalLookup.get(mappingType, className);
|
||||
case Regular:
|
||||
return regularLookup.get(mappingType, className);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid enum value " + nameType);
|
||||
try {
|
||||
switch (nameType) {
|
||||
case Internal:
|
||||
return internalLookup.get(mappingType, className);
|
||||
case Regular:
|
||||
return regularLookup.get(mappingType, className);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid enum value " + nameType);
|
||||
}
|
||||
} catch (Lookup.LookupException e) {
|
||||
throw new ClassNotFoundException("Could not find class \"" + className + "\" with " + nameType.name().toLowerCase() + " name in the " + mappingType.name() + " mapping.");
|
||||
}
|
||||
}
|
||||
|
||||
public static UniversalField getField(FieldInsnNode instruction) throws ClassNotFoundException,
|
||||
NoSuchFieldException {
|
||||
if (!CoreLoadingPlugin.isObfuscated()) {
|
||||
try {
|
||||
return classForName(NameType.Internal, MappingType.MCP, instruction.owner).getField(MappingType.MCP, instruction.name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassNotFoundException("Could not find the class " + instruction.owner + " in the MCP mappings. Are you sure it's a Minecraft class? (we're in dev, cannot use SRG or Notch here).");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return classForName(NameType.Internal, MappingType.SRG, instruction.owner).getField(MappingType.SRG, instruction.name);
|
||||
} catch (ClassNotFoundException e) {
|
||||
try {
|
||||
return classForName(NameType.Internal, MappingType.Notch, instruction.owner).getField(MappingType.Notch, instruction.name);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new ClassNotFoundException("Could not find the class " + instruction.owner + " neither in the SRG nor in the Notch mappings. Are you sure it's a Minecraft class? (we're in obf, cannot use MCP here)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static UniversalMethod getMethod(MethodInsnNode instruction)
|
||||
throws ClassNotFoundException, NoSuchMethodException {
|
||||
if (!CoreLoadingPlugin.isObfuscated()) {
|
||||
try {
|
||||
return classForName(NameType.Internal, MappingType.MCP, instruction.owner).getMethod(MappingType.MCP, instruction.name, instruction.desc);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ClassNotFoundException("Could not find the class " + instruction.owner + " in the MCP mappings. Are you sure it's a Minecraft class? (we're in dev, cannot use SRG or Notch here).");
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return classForName(NameType.Internal, MappingType.SRG, instruction.owner).getMethod(MappingType.SRG, instruction.name, instruction.desc);
|
||||
} catch (ClassNotFoundException e) {
|
||||
try {
|
||||
return classForName(NameType.Internal, MappingType.Notch, instruction.owner).getMethod(MappingType.Notch, instruction.name, instruction.desc);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw new ClassNotFoundException("Could not find the class " + instruction.owner + " neither in the SRG nor in the Notch mappings. Are you sure it's a Minecraft class? (we're in obf, cannot use MCP here)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.falsepattern.lib.reflection;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class ReflectionUtil {
|
||||
private static final Field f_modifiers;
|
||||
static {
|
||||
try {
|
||||
f_modifiers = Field.class.getDeclaredField("modifiers");
|
||||
} catch (NoSuchFieldException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
f_modifiers.setAccessible(true);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static void jailBreak(Field field) {
|
||||
field.setAccessible(true);
|
||||
f_modifiers.set(field, field.getModifiers() & ~Modifier.FINAL);
|
||||
}
|
||||
|
||||
public static void jailBreak(Method method) {
|
||||
method.setAccessible(true);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package com.falsepattern.lib.reflection;
|
||||
|
||||
import com.falsepattern.lib.reflection.storage.Lookup;
|
||||
import com.falsepattern.lib.reflection.types.MappingType;
|
||||
import com.falsepattern.lib.reflection.types.NameType;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class UniversalClass {
|
||||
@Getter
|
||||
public final MappedString internalName;
|
||||
@Getter
|
||||
public final MappedString regularName;
|
||||
|
||||
private final Lookup<UniversalField> fields = new Lookup<>();
|
||||
private final Lookup<UniversalMethod> methods = new Lookup<>();
|
||||
|
||||
public UniversalClass(String[] names, Map<String, String> stringPool) {
|
||||
internalName = new MappedString(names, 0, 1, (str) -> str, stringPool);
|
||||
regularName = new MappedString(names, 0, 1, (str) -> str.replace('/', '.'), stringPool);
|
||||
}
|
||||
|
||||
public void addField(UniversalField field) {
|
||||
fields.unwrap(field.name, field);
|
||||
}
|
||||
|
||||
public void addMethod(UniversalMethod method) {
|
||||
methods.unwrap(method.name, method);
|
||||
}
|
||||
|
||||
public String getName(@NonNull NameType nameType, @NonNull MappingType mappingType) {
|
||||
switch (nameType) {
|
||||
case Internal:
|
||||
return internalName.get(mappingType);
|
||||
case Regular:
|
||||
return regularName.get(mappingType);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid enum value " + nameType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.falsepattern.lib.reflection;
|
||||
|
||||
import com.falsepattern.lib.reflection.types.MappingType;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class UniversalField {
|
||||
@Getter
|
||||
public final MappedString name;
|
||||
|
||||
public UniversalField(String[] names, Map<String, String> stringPool) {
|
||||
name = new MappedString(names, 0, 1, (str) -> str.substring(str.lastIndexOf('/') + 1), stringPool);
|
||||
}
|
||||
|
||||
public String getName(@NonNull MappingType mappingType) {
|
||||
return name.get(mappingType);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.falsepattern.lib.reflection;
|
||||
|
||||
import com.falsepattern.lib.reflection.types.MappingType;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class UniversalMethod {
|
||||
@Getter
|
||||
public final MappedString name;
|
||||
@Getter
|
||||
public final MappedString descriptor;
|
||||
|
||||
public UniversalMethod(String[] names, Map<String, String> stringPool) {
|
||||
name = new MappedString(names, 0, 2, (str) -> str.substring(str.lastIndexOf('/') + 1), stringPool);
|
||||
descriptor = new MappedString(names, 1, 2, (str) -> str, stringPool);
|
||||
}
|
||||
|
||||
public String getName(@NonNull MappingType mappingType) {
|
||||
return name.get(mappingType);
|
||||
}
|
||||
|
||||
public String getDescriptor(@NonNull MappingType mappingType) {
|
||||
return descriptor.get(mappingType);
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,77 @@
|
||||
package com.falsepattern.lib.reflection.storage;
|
||||
|
||||
import com.falsepattern.lib.reflection.MappedString;
|
||||
import com.falsepattern.lib.reflection.types.MappingType;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NonNull;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.val;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@EqualsAndHashCode
|
||||
public class Lookup<T> {
|
||||
private final Set<T> values = new HashSet<>();
|
||||
private final Map<String, T> notch = new HashMap<>();
|
||||
private final Map<String, T> srg = new HashMap<>();
|
||||
private final Map<String, T> mcp = new HashMap<>();
|
||||
|
||||
public void unwrap(MappedString mappedString, T value) {
|
||||
public boolean contains(T value) {
|
||||
return values.contains(value);
|
||||
}
|
||||
|
||||
public void unwrap(@NonNull MappedString mappedString, @NonNull T value) {
|
||||
if (contains(value)) {
|
||||
//Collision avoidance.
|
||||
values.remove(value);
|
||||
removeFirst(notch.entrySet().iterator(), value);
|
||||
removeFirst(srg.entrySet().iterator(), value);
|
||||
removeFirst(mcp.entrySet().iterator(), value);
|
||||
}
|
||||
values.add(value);
|
||||
notch.put(mappedString.notch, value);
|
||||
srg.put(mappedString.srg, value);
|
||||
mcp.put(mappedString.mcp, value);
|
||||
}
|
||||
|
||||
public T get(@NonNull MappingType mappingType, @NonNull String key) {
|
||||
public T get(MappingType mappingType, String key) throws LookupException {
|
||||
T result;
|
||||
switch (mappingType) {
|
||||
case Notch:
|
||||
return notch.get(key);
|
||||
result = notch.get(key);
|
||||
break;
|
||||
case SRG:
|
||||
return srg.get(key);
|
||||
result = srg.get(key);
|
||||
break;
|
||||
case MCP:
|
||||
return mcp.get(key);
|
||||
result = mcp.get(key);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid enum value " + mappingType);
|
||||
}
|
||||
if (result == null) {
|
||||
throw new LookupException("No such key " + key + " in " + mappingType);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <T> void removeFirst(Iterator<Map.Entry<String, T>> iterator, T value) {
|
||||
while (iterator.hasNext()) {
|
||||
val entry = iterator.next();
|
||||
if (entry.getValue().equals(value)) {
|
||||
iterator.remove();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class LookupException extends Exception {
|
||||
public LookupException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
-3
@@ -1,9 +1,10 @@
|
||||
package com.falsepattern.lib.reflection;
|
||||
package com.falsepattern.lib.reflection.storage;
|
||||
|
||||
import com.falsepattern.lib.reflection.types.MappingType;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@@ -14,6 +15,7 @@ import java.util.function.Function;
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@Getter
|
||||
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class MappedString {
|
||||
public final String notch;
|
||||
public final String srg;
|
||||
@@ -24,7 +26,13 @@ public class MappedString {
|
||||
mcp = stringPool.computeIfAbsent(remapper.apply(source[offset + stride * 2]), (str) -> str);
|
||||
}
|
||||
|
||||
public String get(@NonNull MappingType type) {
|
||||
public static MappedString fuse(MappedString a, MappedString b, String delimiter, Map<String, String> stringPool) {
|
||||
return new MappedString(stringPool.computeIfAbsent(a.notch + delimiter + b.notch, (str) -> str),
|
||||
stringPool.computeIfAbsent(a.srg + delimiter + b.srg , (str) -> str),
|
||||
stringPool.computeIfAbsent(a.mcp + delimiter + b.mcp , (str) -> str));
|
||||
}
|
||||
|
||||
public String get(MappingType type) {
|
||||
switch (type) {
|
||||
case Notch:
|
||||
return notch;
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.falsepattern.lib.reflection.types;
|
||||
|
||||
import com.falsepattern.lib.internal.CoreLoadingPlugin;
|
||||
import com.falsepattern.lib.reflection.storage.Lookup;
|
||||
import com.falsepattern.lib.reflection.storage.MappedString;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@ToString(onlyExplicitlyIncluded = true)
|
||||
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
|
||||
public class UniversalClass {
|
||||
@Getter
|
||||
@ToString.Include
|
||||
@EqualsAndHashCode.Include
|
||||
public final MappedString internalName;
|
||||
@Getter
|
||||
@ToString.Include
|
||||
@EqualsAndHashCode.Include
|
||||
public final MappedString regularName;
|
||||
|
||||
private final Lookup<UniversalField> fields = new Lookup<>();
|
||||
private final Lookup<UniversalMethod> methods = new Lookup<>();
|
||||
|
||||
private Class<?> javaClassCache = null;
|
||||
@Getter
|
||||
private MappingType realClassMapping = null;
|
||||
|
||||
public UniversalClass(String[] names, Map<String, String> stringPool) {
|
||||
internalName = new MappedString(names, 0, 1, (str) -> str, stringPool);
|
||||
regularName = new MappedString(names, 0, 1, (str) -> str.replace('/', '.'), stringPool);
|
||||
}
|
||||
|
||||
public void addField(UniversalField field) {
|
||||
if (field.parent != this) {
|
||||
throw new IllegalArgumentException("Field's parent is not this class");
|
||||
}
|
||||
fields.unwrap(field.name, field);
|
||||
}
|
||||
|
||||
public void addMethod(UniversalMethod method) {
|
||||
if (method.parent != this) {
|
||||
throw new IllegalArgumentException("Method's parent is not this class");
|
||||
}
|
||||
methods.unwrap(method.fusedNameDescriptor, method);
|
||||
}
|
||||
|
||||
public Class<?> asJavaClass() throws ClassNotFoundException {
|
||||
if (javaClassCache != null) {
|
||||
return javaClassCache;
|
||||
}
|
||||
if (!CoreLoadingPlugin.isObfuscated()) {
|
||||
javaClassCache = Class.forName(regularName.mcp);
|
||||
realClassMapping = MappingType.MCP;
|
||||
} else {
|
||||
try {
|
||||
javaClassCache = Class.forName(regularName.srg);
|
||||
realClassMapping = MappingType.SRG;
|
||||
} catch (ClassNotFoundException e) {
|
||||
//This should never happen, but we attempt recovery anyway
|
||||
try {
|
||||
javaClassCache = Class.forName(regularName.notch);
|
||||
realClassMapping = MappingType.Notch;
|
||||
} catch (ClassNotFoundException ex) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
return javaClassCache;
|
||||
}
|
||||
|
||||
public String getName(NameType nameType, MappingType mappingType) {
|
||||
switch (nameType) {
|
||||
case Internal:
|
||||
return internalName.get(mappingType);
|
||||
case Regular:
|
||||
return regularName.get(mappingType);
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid enum value " + nameType);
|
||||
}
|
||||
}
|
||||
|
||||
public UniversalField getField(MappingType mappingType, String fieldName) throws NoSuchFieldException {
|
||||
try {
|
||||
return fields.get(mappingType, fieldName);
|
||||
} catch (Lookup.LookupException e) {
|
||||
throw new NoSuchFieldException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public UniversalMethod getMethod(MappingType mappingType, String methodName, String methodDescriptor) throws NoSuchMethodException {
|
||||
try {
|
||||
return methods.get(mappingType, methodName + methodDescriptor);
|
||||
} catch (Lookup.LookupException e) {
|
||||
throw new NoSuchMethodException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.falsepattern.lib.reflection.types;
|
||||
|
||||
import com.falsepattern.lib.reflection.ReflectionUtil;
|
||||
import com.falsepattern.lib.reflection.storage.MappedString;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.val;
|
||||
import org.objectweb.asm.tree.FieldInsnNode;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class UniversalField {
|
||||
@Getter
|
||||
@ToString.Exclude
|
||||
@EqualsAndHashCode.Exclude
|
||||
public final UniversalClass parent;
|
||||
|
||||
@Getter
|
||||
public final MappedString name;
|
||||
|
||||
private Field javaFieldCache = null;
|
||||
|
||||
public UniversalField(@NonNull UniversalClass parent, String[] names, Map<String, String> stringPool) {
|
||||
this.parent = parent;
|
||||
parent.addField(this);
|
||||
name = new MappedString(names, 0, 1, (str) -> str.substring(str.lastIndexOf('/') + 1), stringPool);
|
||||
}
|
||||
|
||||
public String getName(MappingType mappingType) {
|
||||
return name.get(mappingType);
|
||||
}
|
||||
|
||||
public Field asJavaField() throws ClassNotFoundException, NoSuchFieldException {
|
||||
if (javaFieldCache != null) {
|
||||
return javaFieldCache;
|
||||
}
|
||||
val parentClass = parent.asJavaClass();
|
||||
javaFieldCache = parentClass.getDeclaredField(getName(parent.realClassMapping()));
|
||||
ReflectionUtil.jailBreak(javaFieldCache);
|
||||
return javaFieldCache;
|
||||
}
|
||||
|
||||
public FieldInsnNode asInstruction(int opcode, MappingType mapping, String descriptor) {
|
||||
return new FieldInsnNode(opcode, parent.getName(NameType.Internal, mapping), getName(mapping), descriptor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.falsepattern.lib.reflection.types;
|
||||
|
||||
import com.falsepattern.lib.reflection.ReflectionUtil;
|
||||
import com.falsepattern.lib.reflection.storage.MappedString;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.val;
|
||||
import org.objectweb.asm.tree.MethodInsnNode;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
@Accessors(fluent = true)
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class UniversalMethod {
|
||||
@Getter
|
||||
@ToString.Exclude
|
||||
@EqualsAndHashCode.Exclude
|
||||
public final UniversalClass parent;
|
||||
|
||||
@Getter
|
||||
public final MappedString name;
|
||||
@Getter
|
||||
public final MappedString descriptor;
|
||||
@Getter
|
||||
public final MappedString fusedNameDescriptor;
|
||||
|
||||
private Method javaMethodCache = null;
|
||||
|
||||
public UniversalMethod(@NonNull UniversalClass parent, String[] names, Map<String, String> stringPool) {
|
||||
this.parent = parent;
|
||||
parent.addMethod(this);
|
||||
name = new MappedString(names, 0, 2, (str) -> str.substring(str.lastIndexOf('/') + 1), stringPool);
|
||||
descriptor = new MappedString(names, 1, 2, (str) -> str, stringPool);
|
||||
fusedNameDescriptor = MappedString.fuse(name, descriptor, "", stringPool);
|
||||
}
|
||||
|
||||
public String getName(MappingType mappingType) {
|
||||
return name.get(mappingType);
|
||||
}
|
||||
|
||||
public String getDescriptor(MappingType mappingType) {
|
||||
return descriptor.get(mappingType);
|
||||
}
|
||||
|
||||
public Method asJavaMethod() throws ClassNotFoundException, NoSuchMethodException {
|
||||
if (javaMethodCache != null) {
|
||||
return javaMethodCache;
|
||||
}
|
||||
val parentClass = parent.asJavaClass();
|
||||
javaMethodCache = parentClass.getDeclaredMethod(getName(parent.realClassMapping()), decodeMethodDescriptor(getDescriptor(parent.realClassMapping())));
|
||||
ReflectionUtil.jailBreak(javaMethodCache);
|
||||
return javaMethodCache;
|
||||
}
|
||||
|
||||
private static Class<?>[] decodeMethodDescriptor(String desc) throws ClassNotFoundException {
|
||||
val result = new ArrayList<Class<?>>();
|
||||
val buf = new StringBuilder();
|
||||
boolean readingReference = false;
|
||||
int arrayDimensions = 0;
|
||||
for (val c: desc.toCharArray()) {
|
||||
Class<?> parsedClass = null;
|
||||
if (readingReference) {
|
||||
if (c == ';') {
|
||||
parsedClass = Class.forName(buf.toString().replace('/', '.'));
|
||||
buf.setLength(0);
|
||||
readingReference = false;
|
||||
} else {
|
||||
buf.append(c);
|
||||
continue;
|
||||
}
|
||||
} else if (c == '(') continue;
|
||||
else if (c == ')') break;
|
||||
else switch (c) {
|
||||
case 'B':
|
||||
parsedClass = byte.class;
|
||||
break;
|
||||
case 'C':
|
||||
parsedClass = char.class;
|
||||
break;
|
||||
case 'D':
|
||||
parsedClass = double.class;
|
||||
break;
|
||||
case 'F':
|
||||
parsedClass = float.class;
|
||||
break;
|
||||
case 'I':
|
||||
parsedClass = int.class;
|
||||
break;
|
||||
case 'J':
|
||||
parsedClass = long.class;
|
||||
break;
|
||||
case 'L':
|
||||
readingReference = true;
|
||||
continue;
|
||||
case 'S':
|
||||
parsedClass = short.class;
|
||||
break;
|
||||
case 'Z':
|
||||
parsedClass = boolean.class;
|
||||
break;
|
||||
case '[':
|
||||
arrayDimensions++;
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < arrayDimensions; i++) {
|
||||
parsedClass = Array.newInstance(parsedClass, 0).getClass();
|
||||
}
|
||||
arrayDimensions = 0;
|
||||
result.add(parsedClass);
|
||||
}
|
||||
return result.toArray(new Class[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is only here for completeness' sake, given that MethodInsnNode itself also has a deprecated yet functional constructor.
|
||||
*/
|
||||
@Deprecated
|
||||
public MethodInsnNode asInstruction(int opcode, MappingType mapping) {
|
||||
return new MethodInsnNode(opcode, parent.getName(NameType.Internal, mapping), getName(mapping), getDescriptor(mapping));
|
||||
}
|
||||
|
||||
public MethodInsnNode asInstruction(int opcode, MappingType mapping, boolean itf) {
|
||||
return new MethodInsnNode(opcode, parent.getName(NameType.Internal, mapping), getName(mapping), getDescriptor(mapping), itf);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user