Created
August 31, 2018 12:16
-
-
Save Daomephsta/3312e53e44d50571ad92bd6d861b45ff to your computer and use it in GitHub Desktop.
ASM Based List implementation - deliberately terrible
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package leviathan143.asmlist; | |
| import java.lang.reflect.Field; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| @SuppressWarnings("unchecked") | |
| public abstract class ASMArray<E> | |
| { | |
| public static final String INTERNAL_NAME = "leviathan143/asmlist/ASMArray"; | |
| public static final String BINARY_NAME = "leviathan143.asmlist.ASMArray"; | |
| private final Map<String, Field> fieldCache = new HashMap<>(); | |
| public E get(int index) | |
| { | |
| try | |
| { | |
| return (E) getField("element" + index).get(this); | |
| } | |
| catch (IllegalArgumentException | IllegalAccessException | SecurityException e) | |
| { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| public E set(int index, E element) | |
| { | |
| try | |
| { | |
| Field f = getField("element" + index); | |
| E prev = (E) f.get(this); | |
| f.set(this, element); | |
| return prev; | |
| } | |
| catch (IllegalArgumentException | IllegalAccessException | SecurityException e) | |
| { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| public abstract int size(); | |
| private Field getField(String name) | |
| { | |
| return fieldCache.computeIfAbsent(name, t -> | |
| { | |
| try | |
| { | |
| Field f = getClass().getDeclaredField(t); | |
| f.setAccessible(true); | |
| return f; | |
| } catch (NoSuchFieldException | SecurityException e) | |
| { | |
| e.printStackTrace(); | |
| } | |
| return null; | |
| }); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package leviathan143.asmlist; | |
| import static org.objectweb.asm.Opcodes.ACC_PRIVATE; | |
| import static org.objectweb.asm.Opcodes.ACC_PUBLIC; | |
| import static org.objectweb.asm.Opcodes.ALOAD; | |
| import static org.objectweb.asm.Opcodes.INVOKESPECIAL; | |
| import static org.objectweb.asm.Opcodes.IRETURN; | |
| import static org.objectweb.asm.Opcodes.RETURN; | |
| import static org.objectweb.asm.Opcodes.V1_8; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.util.HashMap; | |
| import java.util.Map; | |
| import java.util.regex.Matcher; | |
| import java.util.regex.Pattern; | |
| import org.objectweb.asm.ClassWriter; | |
| import org.objectweb.asm.MethodVisitor; | |
| @SuppressWarnings("unchecked") | |
| public class ASMArrayFactory extends ClassLoader | |
| { | |
| private static final ASMArrayFactory INSTANCE = new ASMArrayFactory(); | |
| private static final Map<Integer, Class<? extends ASMArray<?>>> ELEMENT_CLASSES = new HashMap<>(); | |
| private static final Pattern ASM_LIST_CLASS_NAME = Pattern | |
| .compile(ASMArray.BINARY_NAME.replace(".", "\\.") + "(\\d+)"); | |
| public static <E> ASMArray<E> ofSize(int size) | |
| { | |
| try | |
| { | |
| return (ASMArray<E>) INSTANCE.findClass(ASMArray.BINARY_NAME + size).getDeclaredConstructor().newInstance(); | |
| } catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException | |
| | InvocationTargetException | NoSuchMethodException | SecurityException e) | |
| { | |
| throw new RuntimeException(e); | |
| } | |
| } | |
| @Override | |
| public Class<?> findClass(String name) throws ClassNotFoundException | |
| { | |
| Matcher m = ASM_LIST_CLASS_NAME.matcher(name); | |
| if (m.matches()) | |
| return ELEMENT_CLASSES.computeIfAbsent(new Integer(Integer.parseInt(m.group(1))), this::create); | |
| return super.findClass(name); | |
| } | |
| public Class<? extends ASMArray<?>> create(Integer size) | |
| { | |
| ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); | |
| cw.visit(V1_8, ACC_PUBLIC, ASMArray.INTERNAL_NAME + size, | |
| "<E:Ljava/lang/Object;>L" + ASMArray.INTERNAL_NAME + "<TE;>;", ASMArray.INTERNAL_NAME, null); | |
| { | |
| MethodVisitor ctor = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); | |
| ctor.visitCode(); | |
| { | |
| // Push this onto stack | |
| ctor.visitVarInsn(ALOAD, 0); | |
| ctor.visitMethodInsn(INVOKESPECIAL, ASMArray.INTERNAL_NAME, "<init>", "()V", false); | |
| ctor.visitInsn(RETURN); | |
| } | |
| ctor.visitMaxs(0, 0); | |
| ctor.visitEnd(); | |
| for (int e = 0; e < size; e++) | |
| { | |
| cw.visitField(ACC_PRIVATE, "element" + e, "Ljava/lang/Object;", null, null).visitEnd(); | |
| } | |
| MethodVisitor methodSize = cw.visitMethod(ACC_PUBLIC, "size", "()I", null, null); | |
| methodSize.visitCode(); | |
| { | |
| methodSize.visitAnnotation("Ljava/lang/Override;", true).visitEnd(); | |
| methodSize.visitLdcInsn(new Integer(size)); | |
| methodSize.visitInsn(IRETURN); | |
| } | |
| methodSize.visitMaxs(0, 0); | |
| methodSize.visitEnd(); | |
| } | |
| cw.visitEnd(); | |
| byte[] classBytes = cw.toByteArray(); | |
| return (Class<? extends ASMArray<?>>) defineClass(ASMArray.BINARY_NAME + size, classBytes, 0, | |
| classBytes.length); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package leviathan143.asmlist; | |
| import java.util.AbstractList; | |
| public class ASMBasedList<E> extends AbstractList<E> | |
| { | |
| private ASMArray<E> elements; | |
| public ASMBasedList() | |
| { | |
| elements = ASMArrayFactory.ofSize(0); | |
| } | |
| public ASMBasedList(int size) | |
| { | |
| elements = ASMArrayFactory.ofSize(size); | |
| } | |
| @Override | |
| public E get(int index) | |
| { | |
| checkIndexInBounds(index); | |
| return elements.get(index); | |
| } | |
| @Override | |
| public void add(int index, E element) | |
| { | |
| ASMArray<E> old = elements; | |
| elements = ASMArrayFactory.ofSize(old.size() + 1); | |
| checkIndexInBounds(index); | |
| for(int i = 0; i < old.size(); i++) | |
| { | |
| elements.set(i < index ? i : i + 1, old.get(i)); | |
| } | |
| elements.set(index, element); | |
| } | |
| @Override | |
| public E set(int index, E element) | |
| { | |
| checkIndexInBounds(index); | |
| return elements.set(index, element); | |
| } | |
| @Override | |
| public E remove(int index) | |
| { | |
| checkIndexInBounds(index); | |
| E oldVal = elements.get(index); | |
| elements.set(index, null); | |
| ASMArray<E> old = elements; | |
| elements = ASMArrayFactory.ofSize(old.size() - 1); | |
| for(int i = 0; i < old.size(); i++) | |
| { | |
| if(i == index) continue; | |
| elements.set(i < index ? i : i - 1, old.get(i)); | |
| } | |
| return oldVal; | |
| } | |
| private void checkIndexInBounds(int index) | |
| { | |
| if(index < 0 || index >= elements.size()) throw new IndexOutOfBoundsException(String.format("Index: %d, Min: 0, Max: %d", index, size() - 1)); | |
| } | |
| @Override | |
| public int size() | |
| { | |
| return elements.size(); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package leviathan143.asmlist; | |
| import java.util.Collections; | |
| import java.util.List; | |
| public class ASMBasedListTest | |
| { | |
| public static void main(String[] args) | |
| { | |
| List<String> list = new ASMBasedList<>(); | |
| Collections.addAll(list, "Hello", "World"); | |
| System.out.println(list); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment