mirror of
https://github.com/GravitLauncher/Launcher
synced 2024-11-15 03:31:15 +03:00
[FIX] InjectClassAcceptor fixes (by radioegor146)
This commit is contained in:
parent
83a3c963d3
commit
d813e714da
3 changed files with 204 additions and 166 deletions
|
@ -71,7 +71,7 @@ task cleanjar(type: Jar, dependsOn: jar) {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
pack project(':LauncherAPI')
|
pack project(':LauncherAPI')
|
||||||
bundle 'org.ow2.asm:asm-commons:7.1'
|
bundle 'org.ow2.asm:asm-commons:7.3.1'
|
||||||
bundle 'mysql:mysql-connector-java:8.0.16'
|
bundle 'mysql:mysql-connector-java:8.0.16'
|
||||||
bundle 'org.postgresql:postgresql:42.2.6'
|
bundle 'org.postgresql:postgresql:42.2.6'
|
||||||
bundle 'org.jline:jline:3.13.1'
|
bundle 'org.jline:jline:3.13.1'
|
||||||
|
|
|
@ -1,26 +1,13 @@
|
||||||
package pro.gravit.launchserver.asm;
|
package pro.gravit.launchserver.asm;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.objectweb.asm.AnnotationVisitor;
|
import org.objectweb.asm.AnnotationVisitor;
|
||||||
import org.objectweb.asm.Opcodes;
|
import org.objectweb.asm.Opcodes;
|
||||||
import org.objectweb.asm.Type;
|
import org.objectweb.asm.Type;
|
||||||
import org.objectweb.asm.tree.AnnotationNode;
|
import org.objectweb.asm.tree.*;
|
||||||
import org.objectweb.asm.tree.ClassNode;
|
|
||||||
import org.objectweb.asm.tree.FieldInsnNode;
|
|
||||||
import org.objectweb.asm.tree.FieldNode;
|
|
||||||
import org.objectweb.asm.tree.InsnList;
|
|
||||||
import org.objectweb.asm.tree.InsnNode;
|
|
||||||
import org.objectweb.asm.tree.LdcInsnNode;
|
|
||||||
import org.objectweb.asm.tree.MethodInsnNode;
|
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
|
||||||
import org.objectweb.asm.tree.TypeInsnNode;
|
|
||||||
import org.objectweb.asm.tree.VarInsnNode;
|
|
||||||
|
|
||||||
import pro.gravit.launchserver.binary.BuildContext;
|
import pro.gravit.launchserver.binary.BuildContext;
|
||||||
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
|
import pro.gravit.launchserver.binary.tasks.MainBuildTask;
|
||||||
|
@ -33,167 +20,197 @@ public class InjectClassAcceptor implements MainBuildTask.ASMTransformer {
|
||||||
public InjectClassAcceptor(Map<String, Object> values) {
|
public InjectClassAcceptor(Map<String, Object> values) {
|
||||||
this.values = values;
|
this.values = values;
|
||||||
}
|
}
|
||||||
private static final List<Class<?>> zPrimitivesList = Arrays.asList(java.lang.Boolean.class, java.lang.Character.class,
|
|
||||||
|
private static final List<Class<?>> primitiveClasses = Arrays.asList(java.lang.Boolean.class, java.lang.Character.class,
|
||||||
java.lang.Byte.class, java.lang.Short.class, java.lang.Integer.class, java.lang.Long.class,
|
java.lang.Byte.class, java.lang.Short.class, java.lang.Integer.class, java.lang.Long.class,
|
||||||
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
|
java.lang.Float.class, java.lang.Double.class, java.lang.String.class);
|
||||||
private static final String INJ_DESC = Type.getDescriptor(LauncherInject.class);
|
private static final String INJECTED_FIELD_DESC = Type.getDescriptor(LauncherInject.class);
|
||||||
private static final String INJ_C_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
|
private static final String INJECTED_CONSTRUCTOR_DESC = Type.getDescriptor(LauncherInjectionConstructor.class);
|
||||||
private static final List<String> cPrimitivesList = Arrays.asList("I", "V", "Z", "B", "C", "S", "D", "F", "J", Type.getDescriptor(String.class));
|
private static final List<String> primitiveDescriptors = Arrays.asList(Type.INT_TYPE.getDescriptor(),
|
||||||
|
Type.VOID_TYPE.getDescriptor(), Type.BOOLEAN_TYPE.getDescriptor(), Type.BYTE_TYPE.getDescriptor(),
|
||||||
private static void visit(ClassNode cn, Map<String, Object> object) {
|
Type.CHAR_TYPE.getDescriptor(), Type.SHORT_TYPE.getDescriptor(), Type.DOUBLE_TYPE.getDescriptor(),
|
||||||
MethodNode clinit = cn.methods.stream().filter(methodNode ->
|
Type.FLOAT_TYPE.getDescriptor(), Type.LONG_TYPE.getDescriptor(), Type.getDescriptor(String.class));
|
||||||
"<clinit>".equals(methodNode.name)).findFirst().orElseGet(() -> {
|
|
||||||
MethodNode ret = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC, "<clinit>", "()V", null, null);
|
private static void visit(ClassNode classNode, Map<String, Object> values) {
|
||||||
ret.instructions.add(new InsnNode(Opcodes.RETURN));
|
MethodNode clinitMethod = classNode.methods.stream().filter(methodNode -> "<clinit>".equals(methodNode.name))
|
||||||
cn.methods.add(ret);
|
.findFirst().orElseGet(() -> {
|
||||||
return ret;
|
MethodNode newClinitMethod = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
|
||||||
});
|
"<clinit>", "()V", null, null);
|
||||||
List<MethodNode> constructors = cn.methods.stream().filter(e -> "<init>".equals(e.name)).collect(Collectors.toList());
|
newClinitMethod.instructions.add(new InsnNode(Opcodes.RETURN));
|
||||||
MethodNode init = constructors.stream().filter(e -> e != null && e.invisibleAnnotations != null && e.invisibleAnnotations.stream().anyMatch(f -> INJ_C_DESC.equals(f.desc))).findFirst()
|
classNode.methods.add(newClinitMethod);
|
||||||
.orElseGet(() -> constructors.stream().filter(e -> e.desc.equals("()V")).findFirst().orElse(null));
|
return newClinitMethod;
|
||||||
cn.fields.stream().filter(e -> e.invisibleAnnotations != null)
|
|
||||||
.filter(e -> !e.invisibleAnnotations.isEmpty() && e.invisibleAnnotations.stream().anyMatch(f -> f.desc.equals(INJ_DESC))).forEach(e -> {
|
|
||||||
// Notice that fields that will be used with this algo should not have default
|
|
||||||
// value by = ...;
|
|
||||||
AnnotationNode n = e.invisibleAnnotations.stream().filter(f -> INJ_DESC.equals(f.desc)).findFirst()
|
|
||||||
.get();
|
|
||||||
e.invisibleAnnotations.removeIf(f -> INJ_DESC.equals(f.desc));
|
|
||||||
AtomicReference<String> valueName = new AtomicReference<>(null);
|
|
||||||
n.accept(new AnnotationVisitor(Opcodes.ASM7) {
|
|
||||||
@Override
|
|
||||||
public void visit(final String name, final Object value) {
|
|
||||||
if ("value".equals(name)) {
|
|
||||||
if (value.getClass() != String.class)
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Invalid Annotation with value class " + e.getClass().getName());
|
|
||||||
valueName.set(value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (valueName.get() == null)
|
|
||||||
throw new IllegalArgumentException("Annotation should always contains 'value' key");
|
|
||||||
if (object.containsKey(valueName.get())) {
|
|
||||||
Object val = object.get(valueName.get());
|
|
||||||
if ((e.access & Opcodes.ACC_STATIC) != 0)
|
|
||||||
if (cPrimitivesList.contains(e.desc) && zPrimitivesList.contains(val.getClass()))
|
|
||||||
e.value = val;
|
|
||||||
else {
|
|
||||||
List<FieldInsnNode> nodes = Arrays.stream(clinit.instructions.toArray()).filter(p -> p instanceof FieldInsnNode && p.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
|
|
||||||
.filter(p -> p.owner.equals(cn.name) && p.name.equals(e.name) && p.desc.equals(e.desc)).collect(Collectors.toList());
|
|
||||||
InsnList injector = new InsnList();
|
|
||||||
pushInjector(injector, val, e);
|
|
||||||
if (nodes.isEmpty()) {
|
|
||||||
injector.insert(new InsnNode(Opcodes.ICONST_0));
|
|
||||||
injector.add(new FieldInsnNode(Opcodes.PUTSTATIC, cn.name, e.name, e.desc));
|
|
||||||
Arrays.stream(clinit.instructions.toArray()).filter(p -> p.getOpcode() == Opcodes.RETURN).forEach(p -> clinit.instructions.insertBefore(p, injector));
|
|
||||||
} else
|
|
||||||
for (FieldInsnNode node : nodes) clinit.instructions.insertBefore(node, injector);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (init == null) throw new IllegalArgumentException("Not found init in target: " + cn.name);
|
|
||||||
List<FieldInsnNode> nodes = Arrays.stream(init.instructions.toArray()).filter(p -> p instanceof FieldInsnNode && p.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
|
|
||||||
.filter(p -> p.owner.equals(cn.name) && p.name.equals(e.name) && p.desc.equals(e.desc)).collect(Collectors.toList());
|
|
||||||
InsnList injector = new InsnList();
|
|
||||||
pushInjector(injector, val, e);
|
|
||||||
if (nodes.isEmpty()) {
|
|
||||||
injector.insert(new VarInsnNode(Opcodes.ALOAD, 0));
|
|
||||||
injector.insert(new InsnNode(Opcodes.ICONST_0));
|
|
||||||
injector.add(new FieldInsnNode(Opcodes.PUTSTATIC, cn.name, e.name, e.desc));
|
|
||||||
Arrays.stream(init.instructions.toArray()).filter(p -> p.getOpcode() == Opcodes.RETURN).forEach(p -> clinit.instructions.insertBefore(p, injector));
|
|
||||||
} else
|
|
||||||
for (FieldInsnNode node : nodes) init.instructions.insertBefore(node, injector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
List<MethodNode> constructors = classNode.methods.stream().filter(method -> "<init>".equals(method.name))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
MethodNode initMethod = constructors.stream().filter(method -> method.invisibleAnnotations != null
|
||||||
|
&& method.invisibleAnnotations.stream().anyMatch(annotation -> INJECTED_CONSTRUCTOR_DESC.equals(annotation.desc))).findFirst()
|
||||||
|
.orElseGet(() -> constructors.stream().filter(method -> method.desc.equals("()V")).findFirst().orElse(null));
|
||||||
|
classNode.fields.forEach(field -> {
|
||||||
|
// Notice that fields that will be used with this algo should not have default
|
||||||
|
// value by = ...;
|
||||||
|
AnnotationNode valueAnnotation = field.invisibleAnnotations.stream()
|
||||||
|
.filter(annotation -> INJECTED_FIELD_DESC.equals(annotation.desc)).findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
if (valueAnnotation == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
field.invisibleAnnotations.remove(valueAnnotation);
|
||||||
|
AtomicReference<String> valueName = new AtomicReference<String>(null);
|
||||||
|
valueAnnotation.accept(new AnnotationVisitor(Opcodes.ASM7) {
|
||||||
|
@Override
|
||||||
|
public void visit(final String name, final Object value) {
|
||||||
|
if ("value".equals(name)) {
|
||||||
|
if (value.getClass() != String.class)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
String.format("Invalid annotation with value class %s", field.getClass().getName()));
|
||||||
|
valueName.set(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (valueName.get() == null) {
|
||||||
|
throw new IllegalArgumentException("Annotation should always contains 'value' key");
|
||||||
|
}
|
||||||
|
if (!values.containsKey(valueName.get())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Object value = values.get(valueName.get());
|
||||||
|
if ((field.access & Opcodes.ACC_STATIC) != 0) {
|
||||||
|
if (primitiveDescriptors.contains(field.desc) && primitiveClasses.contains(value.getClass())) {
|
||||||
|
field.value = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<FieldInsnNode> putStaticNodes = Arrays.stream(clinitMethod.instructions.toArray())
|
||||||
|
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTSTATIC).map(p -> (FieldInsnNode) p)
|
||||||
|
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
|
||||||
|
InsnList setter = serializeValue(value);
|
||||||
|
if (putStaticNodes.isEmpty()) {
|
||||||
|
setter.add(new FieldInsnNode(Opcodes.PUTSTATIC, classNode.name, field.name, field.desc));
|
||||||
|
Arrays.stream(clinitMethod.instructions.toArray()).filter(node -> node.getOpcode() == Opcodes.RETURN)
|
||||||
|
.forEach(node -> clinitMethod.instructions.insertBefore(node, setter));
|
||||||
|
} else {
|
||||||
|
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
|
||||||
|
for (FieldInsnNode fieldInsnNode : putStaticNodes) {
|
||||||
|
clinitMethod.instructions.insertBefore(fieldInsnNode, setter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (initMethod == null) {
|
||||||
|
throw new IllegalArgumentException(String.format("Not found init in target: %s", classNode.name));
|
||||||
|
}
|
||||||
|
List<FieldInsnNode> putFieldNodes = Arrays.stream(initMethod.instructions.toArray())
|
||||||
|
.filter(node -> node instanceof FieldInsnNode && node.getOpcode() == Opcodes.PUTFIELD).map(p -> (FieldInsnNode) p)
|
||||||
|
.filter(node -> node.owner.equals(classNode.name) && node.name.equals(field.name) && node.desc.equals(field.desc)).collect(Collectors.toList());
|
||||||
|
InsnList setter = serializeValue(value);
|
||||||
|
if (putFieldNodes.isEmpty()) {
|
||||||
|
setter.insert(new VarInsnNode(Opcodes.ALOAD, 0));
|
||||||
|
setter.add(new FieldInsnNode(Opcodes.PUTFIELD, classNode.name, field.name, field.desc));
|
||||||
|
Arrays.stream(initMethod.instructions.toArray())
|
||||||
|
.filter(node -> node.getOpcode() == Opcodes.RETURN)
|
||||||
|
.forEach(node -> initMethod.instructions.insertBefore(node, setter));
|
||||||
|
} else {
|
||||||
|
setter.insert(new InsnNode(Type.getType(field.desc).getSize() == 1 ? Opcodes.POP : Opcodes.POP2));
|
||||||
|
for (FieldInsnNode fieldInsnNode : putFieldNodes) {
|
||||||
|
initMethod.instructions.insertBefore(fieldInsnNode, setter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
private static Map<Class<?>, Serializer<?>> serializers = new HashMap<>();
|
||||||
private static void pushInjector(InsnList injector, Object val, FieldNode e) {
|
|
||||||
injector.add(new InsnNode(Opcodes.POP));
|
static {
|
||||||
if (e.desc.equals("Z")) {
|
serializers.put(List.class, new ListSerializer());
|
||||||
if ((Boolean) val) injector.add(new InsnNode(Opcodes.ICONST_1));
|
serializers.put(Map.class, new MapSerializer());
|
||||||
else injector.add(new InsnNode(Opcodes.ICONST_0));
|
serializers.put(byte[].class, new ByteArraySerializer());
|
||||||
} else if (e.desc.equals("C")) {
|
}
|
||||||
injector.add(NodeUtils.push(((Number) val).intValue()));
|
|
||||||
injector.add(new InsnNode(Opcodes.I2C));
|
private interface Serializer<T> {
|
||||||
} else if (e.desc.equals("B")) {
|
InsnList serialize(T value);
|
||||||
injector.add(NodeUtils.push(((Number) val).intValue()));
|
}
|
||||||
injector.add(new InsnNode(Opcodes.I2B));
|
|
||||||
} else if (e.desc.equals("S")) {
|
@SuppressWarnings("unchecked")
|
||||||
injector.add(NodeUtils.push(((Number) val).intValue()));
|
private static InsnList serializeValue(Object value) {
|
||||||
injector.add(new InsnNode(Opcodes.I2S));
|
if (value == null) {
|
||||||
} else if (e.desc.equals("I")) {
|
InsnList insnList = new InsnList();
|
||||||
injector.add(NodeUtils.push(((Number) val).intValue()));
|
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
|
||||||
} else if (e.desc.equals("[B")) {
|
return insnList;
|
||||||
serializebArr(injector, (byte[]) val);
|
}
|
||||||
} else if (e.desc.equals("Ljava/util/List;")) {
|
if (primitiveClasses.contains(value.getClass())) {
|
||||||
if (((List) val).isEmpty()) {
|
InsnList insnList = new InsnList();
|
||||||
injector.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
|
insnList.add(new LdcInsnNode(value));
|
||||||
injector.add(new InsnNode(Opcodes.DUP));
|
return insnList;
|
||||||
injector.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
|
}
|
||||||
} else {
|
for (Map.Entry<Class<?>, Serializer<?>> serializerEntry : serializers.entrySet()) {
|
||||||
Class<?> c = ((List) val).get(0).getClass();
|
if (serializerEntry.getKey().isInstance(value)) {
|
||||||
if (c == byte[].class)
|
return ((Serializer) serializerEntry.getValue()).serialize(value);
|
||||||
serializeListbArr(injector, (List<byte[]>) val);
|
|
||||||
else if (c == String.class)
|
|
||||||
serializeListString(injector, (List<String>) val);
|
|
||||||
else
|
|
||||||
throw new UnsupportedOperationException("Unsupported class" + c.getName());
|
|
||||||
}
|
}
|
||||||
} else if (e.desc.equals("Ljava/util/Map;")) {
|
}
|
||||||
serializeMap(injector, (Map<String, String>) val);
|
throw new UnsupportedOperationException(String.format("Serialization of type %s is not supported",
|
||||||
} else {
|
value.getClass()));
|
||||||
if (!cPrimitivesList.contains(e.desc) || !zPrimitivesList.contains(val.getClass()))
|
}
|
||||||
throw new UnsupportedOperationException("Unsupported class");
|
|
||||||
injector.add(new LdcInsnNode(val));
|
private static class ListSerializer implements Serializer<List> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InsnList serialize(List value) {
|
||||||
|
InsnList insnList = new InsnList();
|
||||||
|
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(ArrayList.class)));
|
||||||
|
insnList.add(new InsnNode(Opcodes.DUP));
|
||||||
|
insnList.add(NodeUtils.push(value.size()));
|
||||||
|
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(ArrayList.class), "<init>",
|
||||||
|
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false));
|
||||||
|
for (Object object : value) {
|
||||||
|
insnList.add(new InsnNode(Opcodes.DUP));
|
||||||
|
insnList.add(serializeValue(object));
|
||||||
|
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(List.class), "add",
|
||||||
|
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.getType(Object.class)), true));
|
||||||
|
insnList.add(new InsnNode(Opcodes.POP));
|
||||||
|
}
|
||||||
|
return insnList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void serializeMap(InsnList inj, Map<String, String> map) {
|
private static class MapSerializer implements Serializer<Map> {
|
||||||
inj.add(new TypeInsnNode(Opcodes.NEW, "java/util/HashMap"));
|
|
||||||
inj.add(new InsnNode(Opcodes.DUP)); // +1
|
@Override
|
||||||
inj.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/HashMap", "<init>", "()V"));
|
public InsnList serialize(Map value) {
|
||||||
map.forEach((k, v) -> {
|
InsnList insnList = new InsnList();
|
||||||
inj.add(new InsnNode(Opcodes.DUP)); // +1-1
|
insnList.add(new TypeInsnNode(Opcodes.NEW, Type.getInternalName(value.getClass())));
|
||||||
inj.add(NodeUtils.getSafeStringInsnList(k));
|
insnList.add(new InsnNode(Opcodes.DUP));
|
||||||
inj.add(NodeUtils.getSafeStringInsnList(v));
|
insnList.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, Type.getInternalName(value.getClass()), "<init>",
|
||||||
inj.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", true));
|
Type.getMethodDescriptor(Type.VOID_TYPE), false));
|
||||||
inj.add(new InsnNode(Opcodes.POP));
|
for (Object entryObject : value.entrySet()) {
|
||||||
});
|
Map.Entry entry = (Map.Entry) entryObject;
|
||||||
|
insnList.add(new InsnNode(Opcodes.DUP));
|
||||||
|
insnList.add(serializeValue(entry.getKey()));
|
||||||
|
insnList.add(serializeValue(entry.getValue()));
|
||||||
|
insnList.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, Type.getInternalName(Map.class), "put",
|
||||||
|
Type.getMethodDescriptor(Type.getType(Object.class), Type.getType(Object.class), Type.getType(Object.class)),
|
||||||
|
true));
|
||||||
|
insnList.add(new InsnNode(Opcodes.POP));
|
||||||
|
}
|
||||||
|
return insnList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void serializebArr(InsnList injector, byte[] val) {
|
private static class ByteArraySerializer implements Serializer<byte[]> {
|
||||||
injector.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false));
|
|
||||||
injector.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(val)));
|
|
||||||
injector.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "(Ljava/lang/String;)[B", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void serializeListbArr(InsnList inj, List<byte[]> val) {
|
@Override
|
||||||
inj.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
|
public InsnList serialize(byte[] value) {
|
||||||
inj.add(new InsnNode(Opcodes.DUP)); // +1
|
InsnList insnList = new InsnList();
|
||||||
inj.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
|
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, Type.getInternalName(Base64.class),
|
||||||
for (byte[] value : val) {
|
"getDecoder", Type.getMethodDescriptor(Type.getType(Base64.Decoder.class)), false));
|
||||||
inj.add(new InsnNode(Opcodes.DUP)); // +1-1
|
insnList.add(NodeUtils.getSafeStringInsnList(Base64.getEncoder().encodeToString(value)));
|
||||||
serializebArr(inj, value);
|
insnList.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Base64.Decoder.class),
|
||||||
inj.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true));
|
"decode", Type.getMethodDescriptor(Type.getType(byte[].class), Type.getType(String.class)),
|
||||||
inj.add(new InsnNode(Opcodes.POP));
|
false));
|
||||||
}
|
return insnList;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void serializeListString(InsnList inj, List<String> val) {
|
|
||||||
inj.add(new TypeInsnNode(Opcodes.NEW, "java/util/ArrayList"));
|
|
||||||
inj.add(new InsnNode(Opcodes.DUP)); // +1
|
|
||||||
inj.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V"));
|
|
||||||
for (String value : val) {
|
|
||||||
inj.add(new InsnNode(Opcodes.DUP)); // +1-1
|
|
||||||
inj.add(NodeUtils.getSafeStringInsnList(value));
|
|
||||||
inj.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true));
|
|
||||||
inj.add(new InsnNode(Opcodes.POP));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transform(ClassNode cn, String classname, BuildContext context) {
|
public void transform(ClassNode classNode, String className, BuildContext context) {
|
||||||
visit(cn, values);
|
visit(classNode, values);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,9 @@
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class ASMTransformersTest {
|
public class ASMTransformersTest {
|
||||||
|
@ -34,7 +36,11 @@ public void rawDefineClass(String name, byte[] bytes, int offset, int length)
|
||||||
public static class TestClass
|
public static class TestClass
|
||||||
{
|
{
|
||||||
@LauncherInject(value = "testprop")
|
@LauncherInject(value = "testprop")
|
||||||
public static int test = 1;
|
public int test;
|
||||||
|
@LauncherInject(value = "testprop2")
|
||||||
|
public List<String> s;
|
||||||
|
@LauncherInject(value = "testprop3")
|
||||||
|
public Map<String, String> map;
|
||||||
}
|
}
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void prepare() throws Exception {
|
public static void prepare() throws Exception {
|
||||||
|
@ -49,6 +55,14 @@ void testASM() throws Exception
|
||||||
node.name = "ASMTestClass";
|
node.name = "ASMTestClass";
|
||||||
Map<String, Object> map = new HashMap<>();
|
Map<String, Object> map = new HashMap<>();
|
||||||
map.put("testprop", 1234);
|
map.put("testprop", 1234);
|
||||||
|
List<String> strings = new ArrayList<>();
|
||||||
|
strings.add("a");
|
||||||
|
strings.add("b");
|
||||||
|
map.put("testprop2", strings);
|
||||||
|
Map<String, String> byteMap = new HashMap<>();
|
||||||
|
byteMap.put("a", "TEST A");
|
||||||
|
byteMap.put("b", "TEST B");
|
||||||
|
map.put("testprop3", byteMap);
|
||||||
InjectClassAcceptor injectClassAcceptor = new InjectClassAcceptor(map);
|
InjectClassAcceptor injectClassAcceptor = new InjectClassAcceptor(map);
|
||||||
injectClassAcceptor.transform(node, "ASMTestClass", null);
|
injectClassAcceptor.transform(node, "ASMTestClass", null);
|
||||||
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
|
||||||
|
@ -56,8 +70,15 @@ void testASM() throws Exception
|
||||||
byte[] bytes = writer.toByteArray();
|
byte[] bytes = writer.toByteArray();
|
||||||
classLoader.rawDefineClass("ASMTestClass", bytes, 0, bytes.length);
|
classLoader.rawDefineClass("ASMTestClass", bytes, 0, bytes.length);
|
||||||
Class<?> clazz = classLoader.loadClass("ASMTestClass");
|
Class<?> clazz = classLoader.loadClass("ASMTestClass");
|
||||||
|
Object instance = clazz.newInstance();
|
||||||
Field field = clazz.getField("test");
|
Field field = clazz.getField("test");
|
||||||
Object result = field.get(null);
|
Object result = field.get(instance);
|
||||||
Assertions.assertEquals(1234, (int) (Integer) result);
|
Assertions.assertEquals(1234, (int) (Integer) result);
|
||||||
|
field = clazz.getField("s");
|
||||||
|
result = field.get(instance);
|
||||||
|
Assertions.assertEquals(strings, result);
|
||||||
|
field = clazz.getField("map");
|
||||||
|
result = field.get(instance);
|
||||||
|
Assertions.assertEquals(byteMap, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue