diff --git a/.gitignore b/.gitignore
index d0485ae..9a33f88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,5 @@
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
+.idea/
+.iml
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..9182bc3
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,104 @@
+
+
+ 4.0.0
+
+ eu.mikroskeem
+ bytecodehackery
+ 1.0-SNAPSHOT
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.4.3
+
+
+ package
+
+ shade
+
+
+
+
+
+
+ *:*
+
+ META-INF/LICENSE
+ META-INF/DEPENDENCIES
+ META-INF/NOTICE
+ META-INF/*.SF
+ META-INF/*.DSA
+ META-INF/*.RSA
+ META-INF/*.properties
+ META-INF/maven/**
+ META-INF/services/**
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ true
+ eu.mikroskeem.redpowder.leviathan.Main
+
+
+
+
+
+
+
+ src/main/resources
+ true
+
+
+ clean compile package
+
+
+
+
+ org.ow2.asm
+ asm-all
+ 5.1
+
+
+
\ No newline at end of file
diff --git a/src/main/java/eu/mikroskeem/bytecodehackery/GeneratedClassLoader.java b/src/main/java/eu/mikroskeem/bytecodehackery/GeneratedClassLoader.java
new file mode 100644
index 0000000..889d59d
--- /dev/null
+++ b/src/main/java/eu/mikroskeem/bytecodehackery/GeneratedClassLoader.java
@@ -0,0 +1,63 @@
+package eu.mikroskeem.bytecodehackery;
+
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class GeneratedClassLoader extends ClassLoader {
+
+ /*
+ * Note: "Stolen: from Techcable's Event4j,
+ * https://github.com/Techcable/Event4J/blob/master/src/main/java/net/techcable/event4j/asm/ASMEventExecutorFactory.java
+ */
+
+ private static final ConcurrentMap loaders = new ConcurrentHashMap<>();
+ public static GeneratedClassLoader getLoader(ClassLoader parent) {
+ return loaders.computeIfAbsent(Objects.requireNonNull(parent, "Null parent class-loader"), GeneratedClassLoader::new);
+ }
+
+ public Class> defineClass(String name, byte[] data) {
+ name = Objects.requireNonNull(name, "Null name").replace('/', '.');
+ synchronized (getClassLoadingLock(name)) {
+ if (hasClass(name)) throw new IllegalStateException(name + " already defined");
+ Class> c = this.define(name, Objects.requireNonNull(data, "Null data"));
+ if (!c.getName().equals(name)) throw new IllegalArgumentException("class name " + c.getName() + " != requested name " + name);
+ return c;
+ }
+ }
+
+ protected GeneratedClassLoader(ClassLoader parent) {
+ super(parent);
+ }
+
+ private Class> define(String name, byte[] data) {
+ synchronized (getClassLoadingLock(name)) {
+ if (hasClass(name)) throw new IllegalStateException("Already has class: " + name);
+ Class> c;
+ try {
+ c = defineClass(name, data, 0, data.length);
+ } catch (ClassFormatError e) {
+ throw new IllegalArgumentException("Illegal class data", e);
+ }
+ resolveClass(c);
+ return c;
+ }
+ }
+
+ @Override
+ public Object getClassLoadingLock(String name) {
+ return super.getClassLoadingLock(name);
+ }
+
+ public boolean hasClass(String name) {
+ synchronized (getClassLoadingLock(name)) {
+ try {
+ Class.forName(name);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/eu/mikroskeem/bytecodehackery/Main.java b/src/main/java/eu/mikroskeem/bytecodehackery/Main.java
new file mode 100644
index 0000000..3e120af
--- /dev/null
+++ b/src/main/java/eu/mikroskeem/bytecodehackery/Main.java
@@ -0,0 +1,77 @@
+package eu.mikroskeem.bytecodehackery;
+
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+
+import static org.objectweb.asm.Opcodes.*;
+
+public class Main {
+ private Main() throws Exception {
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+
+ /* Generate class */
+ cw.visit(V1_8, ACC_PUBLIC, (getClass().getPackage().getName() +".kek.Topkek").replaceAll("\\.", "/"),
+ null, "java/lang/Object", new String[]{Type.getInternalName(TestInterface.class)});
+
+ /* Generate constructor */
+ MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
+ mv.visitVarInsn(ALOAD, 0);
+
+ /* Must-have for all classes */
+ mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+ mv.visitInsn(RETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+
+ /* Generate toString method */
+ String toStringDesc = String.format("(%s)%s", "", Type.getDescriptor(String.class));
+ mv = cw.visitMethod(ACC_PUBLIC, "toString", toStringDesc, null, null);
+ mv.visitLdcInsn("TOPKEK LMAUUU");
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+
+ /* Generate a() method */
+ String aDesc = String.format("(%s)%s", "", Type.getDescriptor(String.class));
+ mv = cw.visitMethod(ACC_PUBLIC, "a", aDesc, null, null);
+ mv.visitLdcInsn("some a() call");
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+
+
+ mv = cw.visitMethod(ACC_PUBLIC, "b", "()Ljava/util/List;", "()Ljava/util/List;", null);
+ mv.visitCode();
+ mv.visitTypeInsn(NEW, "java/util/ArrayList");
+ mv.visitInsn(DUP);
+ mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "", "()V", false);
+ mv.visitVarInsn(ASTORE, 1);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitLdcInsn("kek");
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitLdcInsn("ayy lmao");
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true);
+ mv.visitInsn(POP);
+ mv.visitVarInsn(ALOAD, 1);
+ mv.visitInsn(ARETURN);
+ mv.visitMaxs(2, 2);
+ mv.visitEnd();
+
+ /* Class end */
+ cw.visitEnd();
+
+ /* Load that class */
+ Class> a = GeneratedClassLoader.getLoader(this.getClass().getClassLoader())
+ .defineClass(getClass().getPackage().getName() +".kek.Topkek", cw.toByteArray());
+
+ TestInterface b = (TestInterface) a.newInstance();
+ System.out.println(b.toString());
+ System.out.println(b.a());
+ System.out.println(b.b());
+ }
+ public static void main(String... a) throws Exception {
+ new Main();
+ }
+}
diff --git a/src/main/java/eu/mikroskeem/bytecodehackery/TestClass.java b/src/main/java/eu/mikroskeem/bytecodehackery/TestClass.java
new file mode 100644
index 0000000..f98c45c
--- /dev/null
+++ b/src/main/java/eu/mikroskeem/bytecodehackery/TestClass.java
@@ -0,0 +1,18 @@
+package eu.mikroskeem.bytecodehackery;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestClass {
+ @Override
+ public String toString() {
+ return "TestClass lel";
+ }
+
+ public List get(){
+ List r = new ArrayList<>();
+ r.add("kek");
+ r.add("kek2");
+ return r;
+ }
+}
diff --git a/src/main/java/eu/mikroskeem/bytecodehackery/TestInterface.java b/src/main/java/eu/mikroskeem/bytecodehackery/TestInterface.java
new file mode 100644
index 0000000..43ad943
--- /dev/null
+++ b/src/main/java/eu/mikroskeem/bytecodehackery/TestInterface.java
@@ -0,0 +1,8 @@
+package eu.mikroskeem.bytecodehackery;
+
+import java.util.List;
+
+public interface TestInterface {
+ String a();
+ List b();
+}