Skip to content

Instantly share code, notes, and snippets.

@Daomephsta
Created August 31, 2018 12:16
Show Gist options
  • Select an option

  • Save Daomephsta/3312e53e44d50571ad92bd6d861b45ff to your computer and use it in GitHub Desktop.

Select an option

Save Daomephsta/3312e53e44d50571ad92bd6d861b45ff to your computer and use it in GitHub Desktop.
ASM Based List implementation - deliberately terrible
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;
});
}
}
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);
}
}
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();
}
}
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