diff --git a/src/main/java/dev/ashhhleyyy/bad/Main.java b/src/main/java/dev/ashhhleyyy/bad/Main.java index 8e467dd..549d4be 100644 --- a/src/main/java/dev/ashhhleyyy/bad/Main.java +++ b/src/main/java/dev/ashhhleyyy/bad/Main.java @@ -2,11 +2,14 @@ package dev.ashhhleyyy.bad; import dev.ashhhleyyy.bad.framework.ClassMaker; import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.LabelNode; import java.lang.reflect.Method; public class Main { public static void main(String[] args) throws Exception { + System.setProperty("ashhhleyyy.bad.framework.dumpClassFiles", "out/bfGenerated"); + System.out.println("Hello world!"); ClassMaker maker = new ClassMaker("dev/ashhhleyyy/runtime/RuntimeClass_"); @@ -24,10 +27,45 @@ public class Main { bytecode.invokestatic( "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I"); bytecode.ireturn(); }); + + builder.method( + Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, + "iAmALoop", + "()V", + null, + new String[0], + bytecode -> { + bytecode.startLocal("i", "I", null, 0); + bytecode.bipush(0); + bytecode.istore(0); + LabelNode loopStart = bytecode.label(); + bytecode.iload(0); + bytecode.bipush(10); + LabelNode loopBreak = bytecode.if_icmpge(); + bytecode.getstatic("java/lang/System", "out", "Ljava/io/PrintStream;"); + bytecode.ldc("i am a loop"); + bytecode.invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + + bytecode.iinc(0, 1); + + bytecode.$goto(loopStart); + bytecode.label(loopBreak); + bytecode.endLocal("i"); + + bytecode.getstatic("java/lang/System", "out", "Ljava/io/PrintStream;"); + bytecode.ldc("i am not a loop"); + bytecode.invokevirtual("java/io/PrintStream", "println", "(Ljava/lang/String;)V"); + + bytecode.$return(); + } + ); }); Method method = cls.getMethod("toBase10", String.class); int i = (int) method.invoke(null, "10101010"); System.out.println(i); + + Method loop = cls.getMethod("iAmALoop"); + loop.invoke(null); } } \ No newline at end of file diff --git a/src/main/java/dev/ashhhleyyy/bad/framework/BytecodeBuilder.java b/src/main/java/dev/ashhhleyyy/bad/framework/BytecodeBuilder.java index e9fc449..567559d 100644 --- a/src/main/java/dev/ashhhleyyy/bad/framework/BytecodeBuilder.java +++ b/src/main/java/dev/ashhhleyyy/bad/framework/BytecodeBuilder.java @@ -1,15 +1,20 @@ package dev.ashhhleyyy.bad.framework; +import java.util.HashMap; +import java.util.Map; + import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldInsnNode; +import org.objectweb.asm.tree.IincInsnNode; import org.objectweb.asm.tree.InsnNode; import org.objectweb.asm.tree.IntInsnNode; import org.objectweb.asm.tree.JumpInsnNode; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.LdcInsnNode; +import org.objectweb.asm.tree.LocalVariableNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TypeInsnNode; @@ -19,13 +24,21 @@ public final class BytecodeBuilder { private final ClassNode classNode; private final MethodNode methodNode; + private final Map locals; + + private LabelNode currentLabel = null; + public BytecodeBuilder(ClassNode classNode, MethodNode methodNode) { this.classNode = classNode; this.methodNode = methodNode; + this.locals = new HashMap<>(); } public void add(AbstractInsnNode insn) { this.methodNode.instructions.add(insn); + if (!(insn instanceof LabelNode)) { + this.currentLabel = null; + } } // Helpers @@ -38,6 +51,28 @@ public final class BytecodeBuilder { this.add(new FieldInsnNode(Opcodes.GETFIELD, this.classNode.name, name, desc)); } + // Local variable management + + public void startLocal(String name, String descriptor, String signature, int index) { + if (this.locals.containsKey(name)) { + throw new IllegalArgumentException("there is already a local called " + name); + } + LabelNode start = this.label(); + LocalVariableNode lv = new LocalVariableNode(name, descriptor, signature, start, null, index); + this.locals.put(name, lv); + } + + public void endLocal(String name) { + LocalVariableNode lv = this.locals.get(name); + if (lv == null) { + throw new IllegalArgumentException("there is no current local variable called " + name); + } + LabelNode end = this.label(); + lv.end = end; + this.methodNode.localVariables.add(lv); + this.locals.remove(name); + } + // Raw instructions public void iadd() { @@ -72,10 +107,22 @@ public final class BytecodeBuilder { this.add(new VarInsnNode(Opcodes.ILOAD, index)); } + public void istore(int index) { + this.add(new VarInsnNode(Opcodes.ISTORE, index)); + } + public void aload(int index) { this.add(new VarInsnNode(Opcodes.ALOAD, index)); } + public void astore(int index) { + this.add(new VarInsnNode(Opcodes.ASTORE, index)); + } + + public void iinc(int index, int amount) { + this.add(new IincInsnNode(index, amount)); + } + public void sipush(int operand) { this.add(new IntInsnNode(Opcodes.SIPUSH, operand)); } @@ -92,8 +139,20 @@ public final class BytecodeBuilder { this.add(new TypeInsnNode(Opcodes.ANEWARRAY, type)); } - public void invokedynamic(String owner, String name, String desc) { - this.add(new MethodInsnNode(Opcodes.INVOKEDYNAMIC, owner, name, desc)); + public void getfield(String owner, String name, String desc) { + this.add(new FieldInsnNode(Opcodes.GETFIELD, owner, name, desc)); + } + + public void getstatic(String owner, String name, String desc) { + this.add(new FieldInsnNode(Opcodes.GETSTATIC, owner, name, desc)); + } + + public void invokevirtual(String owner, String name, String desc) { + this.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, owner, name, desc)); + } + + public void invokeinterface(String owner, String name, String desc) { + this.add(new MethodInsnNode(Opcodes.INVOKEINTERFACE, owner, name, desc)); } public void invokespecial(String owner, String name, String desc) { @@ -104,12 +163,29 @@ public final class BytecodeBuilder { this.add(new MethodInsnNode(Opcodes.INVOKESTATIC, owner, name, desc)); } + private LabelNode getCurrentLabel() { + if (this.currentLabel != null) { + return this.currentLabel; + } else { + this.currentLabel = new LabelNode(new Label()); + return this.currentLabel; + } + } + public LabelNode label() { - return this.label(new LabelNode(new Label())); + return this.label(this.getCurrentLabel()); } public LabelNode label(LabelNode label) { this.add(label); + if (this.currentLabel == null) { + this.currentLabel = label; + } + return label; + } + + public LabelNode $goto(LabelNode label) { + this.add(new JumpInsnNode(Opcodes.GOTO, label)); return label; } @@ -123,6 +199,10 @@ public final class BytecodeBuilder { return label; } + public LabelNode $goto() { + return this.$goto(new LabelNode(new Label())); + } + public LabelNode if_icmpge() { return this.if_icmpge(new LabelNode(new Label())); }