├── .gitignore ├── README.md └── src └── xyz └── jadonfowler └── jvmbg ├── IdentifierType.java ├── JVMBG.java ├── JVMClass.java ├── JVMConstructor.java ├── JVMMethod.java ├── Modifiers.java ├── Variable.java └── test └── BasicClass.java /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | .settings/ 3 | .classpath 4 | .project 5 | asm.* 6 | *.class 7 | *.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #JVM Bytecode Generator 2 | A JVM Bytecode Generator for compilers. 3 | 4 | JVMBG offers an easy-to-use API that can work with lazy or normal compilers. 5 | 6 | Here's a sample of this awesomeness (*Note: This is a major WIP and won't work as expected right now!*): 7 | ```java 8 | //Example 9 | JVMClass clazz = new JVMClass("xyz/jadonfowler/derp/Test"); // public class Test extends java.lang.Object 10 | 11 | JVMConstructor constructor = new JVMConstructor(clazz, Modifiers.PUBLIC); // public Test() 12 | // Creates a field called 'field' and sets it to 12 13 | constructor.createField(new Variable(IdentifierType.INT, "field", 12)); 14 | 15 | clazz.addMethod(constructor); // Finsishes up the constructor bytecode 16 | 17 | JVMMethod method = new JVMMethod("test", Modifiers.PUBLIC, Modifiers.STATIC); // public static void test() 18 | method.createLocalVariable(new Variable(IdentifierType.INT, "variable", 7)); // int variable = 7 19 | method.changeLocalVariable("variable", 12); 20 | 21 | clazz.addMethod(method); // Finishes up the method bytecode 22 | clazz.build(); // Output class files into local directory 23 | ``` -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/IdentifierType.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | //import static org.objectweb.asm.Opcodes.*; 4 | 5 | public enum IdentifierType{ 6 | BYTE("B"), CHAR("C"), STRING("S"), INT("I"), LONG("J"), FLOAT("F"), DOUBLE("D"), BOOLEAN("Z"), VOID("V"); 7 | String descriptor; 8 | 9 | IdentifierType(String s) { 10 | this.descriptor = s; 11 | } 12 | 13 | @Override public String toString() { 14 | return descriptor; 15 | } 16 | 17 | public static IdentifierType getIdentifier(Object o) { 18 | if (o instanceof Integer) return INT; 19 | else if (o instanceof Character) return CHAR; 20 | else if (o instanceof String) return STRING; 21 | else if (o instanceof Byte) return BYTE; 22 | else if (o instanceof Long) return LONG; 23 | else if (o instanceof Float) return FLOAT; 24 | else if (o instanceof Double) return DOUBLE; 25 | else if (o instanceof Boolean) return BOOLEAN; 26 | else return VOID; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/JVMBG.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | public class JVMBG { 4 | public static void main(String[] args) throws Exception { 5 | // Example 6 | System.out.println("JVMBG Test"); 7 | JVMClass clazz = new JVMClass("Test"); // public class Test extends java.lang.Object 8 | JVMConstructor constructor = new JVMConstructor(clazz, Modifiers.PUBLIC); 9 | constructor.createField(new Variable(IdentifierType.INT, "field", 12)); 10 | constructor.finishFields(); 11 | clazz.addMethod(constructor); 12 | 13 | JVMMethod method = new JVMMethod("test", Modifiers.PUBLIC, Modifiers.STATIC); 14 | //int variable = 7 15 | method.createLocalVariable(new Variable(IdentifierType.INT, "variable", 7)); 16 | method.changeLocalVariable("variable", 12); 17 | clazz.addMethod(method); 18 | 19 | clazz.build(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/JVMClass.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | import java.io.*; 4 | import org.objectweb.asm.*; 5 | 6 | public class JVMClass implements Opcodes { 7 | private final int modifiers; 8 | final String name; 9 | final String superClass; 10 | protected static ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); 11 | protected static FieldVisitor fv; 12 | protected static MethodVisitor mv; 13 | protected static AnnotationVisitor av0; 14 | 15 | public JVMClass(String dec) { 16 | this(dec, Modifiers.PUBLIC); 17 | } 18 | 19 | public JVMClass(String name, Modifiers... ms) { 20 | this(name, "java/lang/Object", ms); 21 | } 22 | 23 | public JVMClass(String name, String superClass, Modifiers... ms) { 24 | int m = 0; 25 | for (Modifiers k : ms) 26 | m += k.toACC(); 27 | this.modifiers = m; 28 | this.name = name.replace(".", "/"); 29 | this.superClass = superClass.replace(".", "/"); 30 | cw.visit(52, modifiers, name, null, this.superClass, null); 31 | } 32 | 33 | public JVMClass addMethod(JVMMethod m) { 34 | m.build(); 35 | return this; 36 | } 37 | 38 | public void build() { 39 | cw.visitEnd(); 40 | final byte[] classBytes = cw.toByteArray(); 41 | try (FileOutputStream stream = new FileOutputStream(name + ".class")) { 42 | stream.write(classBytes); 43 | stream.close(); 44 | } 45 | catch (IOException e) { 46 | e.printStackTrace(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/JVMConstructor.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | public class JVMConstructor extends JVMMethod { 4 | private int fieldCount = 0; 5 | private final JVMClass superClass; 6 | private boolean finishedFields = false; 7 | 8 | public JVMConstructor(JVMClass superClass, Modifiers... ms) { 9 | this(superClass, "()V", ms); 10 | } 11 | 12 | public JVMConstructor(JVMClass superClass, String dec, Modifiers... ms) { 13 | super("", dec, ms); 14 | JVMClass.mv.visitVarInsn(ALOAD, 0); 15 | JVMClass.mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); 16 | this.superClass = superClass; 17 | } 18 | 19 | public void createField(Variable v) { 20 | assert!finishedFields : "Fields can not be added after they have been finished!"; 21 | {// Create Field 22 | JVMClass.fv = JVMClass.cw.visitField(0/* wut is dis */, v.getIdentifier(), v.getType().toString(), null, 23 | null); 24 | JVMClass.fv.visitEnd(); 25 | } 26 | JVMClass.mv.visitVarInsn(ALOAD, 0/* wut is dis */); 27 | // Push value 28 | switch (v.getType()) { 29 | case INT: 30 | int value = (int) v.getValue(); 31 | if (Math.abs(value) >= 128) JVMClass.mv.visitIntInsn(SIPUSH, value); 32 | else JVMClass.mv.visitIntInsn(BIPUSH, value); 33 | break; 34 | case STRING: 35 | JVMClass.mv.visitLdcInsn(v.getValue().toString()); 36 | break; 37 | case BOOLEAN: 38 | if ((boolean) v.getValue()) JVMClass.mv.visitInsn(ICONST_1); 39 | else JVMClass.mv.visitInsn(ICONST_0); 40 | break; 41 | case CHAR: 42 | JVMClass.mv.visitIntInsn(BIPUSH, (int) ((char) v.getValue())); // Overcasting? 43 | break; 44 | case LONG: 45 | JVMClass.mv.visitLdcInsn((long) v.getValue()); 46 | break; 47 | case FLOAT: 48 | JVMClass.mv.visitLdcInsn((float) v.getValue()); 49 | break; 50 | case DOUBLE: 51 | JVMClass.mv.visitLdcInsn((double) v.getValue()); 52 | break; 53 | default: 54 | break; 55 | } 56 | // Pop value and store into field 57 | // System.out.println("Field in `" + superClass.name + "` with name of 58 | // `" + v.getIdentifier() + "` of type `" 59 | // + v.getType().toString() + "` with value `" + v.getValue() + "`"); 60 | JVMClass.mv.visitFieldInsn(PUTFIELD, superClass.name, v.getIdentifier(), v.getType().toString()); 61 | fieldCount++; 62 | } 63 | 64 | public void finishFields() { 65 | this.finishedFields = true; 66 | } 67 | 68 | public void callSuperConstructor() { 69 | JVMClass.mv.visitVarInsn(ALOAD, 0); 70 | JVMClass.mv.visitMethodInsn(INVOKESPECIAL, this.superClass.superClass, "", "()V", false); 71 | } 72 | 73 | @Override public void build() { 74 | JVMClass.mv.visitInsn(RETURN); 75 | int argumentCount = this.description.split("\\(")[1].split("\\)")[0].length(); 76 | // System.out.println("Constructor Maxes: " + (1 + fieldCount) + "," + 77 | // (1 + this.variables.size() + argumentCount)); 78 | JVMClass.mv.visitMaxs(1 + fieldCount, 1 + this.variables.size() + argumentCount); 79 | JVMClass.mv.visitEnd(); 80 | JVMClass.mv = null; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/JVMMethod.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | import java.util.*; 4 | import org.objectweb.asm.*; 5 | 6 | public class JVMMethod implements Opcodes { 7 | private final int modifiers; 8 | protected String description; 9 | protected String name; 10 | protected final ArrayList variables = new ArrayList(); 11 | 12 | public JVMMethod(String name, Modifiers... ms) { 13 | this(name, "()V", ms); 14 | } 15 | 16 | public JVMMethod(String name, String dec, Modifiers... ms) { 17 | int m = 0; 18 | for (Modifiers k : ms) 19 | m |= k.toACC(); 20 | this.modifiers = m; 21 | this.description = dec; 22 | this.name = name; 23 | // Start code creation for JIT compilation 24 | JVMClass.mv = JVMClass.cw.visitMethod(modifiers, name, description, null, null); 25 | JVMClass.mv.visitCode(); 26 | } 27 | 28 | public void createLocalVariable(Variable v) { 29 | for (Variable vx : variables) 30 | if (vx.getIdentifier().equals(v.getIdentifier())) throw new IllegalArgumentException( 31 | "There is already a variable in " + name + "() called " + vx.getIdentifier() + "!"); 32 | variables.add(v); 33 | changeLocalVariable(v.getIdentifier(), v.getValue()); 34 | } 35 | 36 | public void changeLocalVariable(String identifier, Object value) { 37 | Variable v = null; 38 | for (Variable vx : variables) { 39 | if (vx.getIdentifier().equals(identifier)) { 40 | v = vx; 41 | break; 42 | } 43 | } 44 | changeLocalVariable(v, value); 45 | } 46 | 47 | public void changeLocalVariable(Variable v, Object value) { 48 | assert v == null : "Variable `" + v + "` not found in " + name + "()"; 49 | int variableIndex = variables.indexOf(v); 50 | switch (v.getType()) { 51 | case INT: 52 | int vi = (int) value; 53 | if (Math.abs(vi) >= 128) JVMClass.mv.visitIntInsn(SIPUSH, vi); 54 | else JVMClass.mv.visitIntInsn(BIPUSH, vi); 55 | JVMClass.mv.visitVarInsn(ISTORE, variableIndex); 56 | break; 57 | case STRING: 58 | JVMClass.mv.visitLdcInsn(value.toString()); 59 | JVMClass.mv.visitVarInsn(ASTORE, variableIndex); 60 | break; 61 | case BOOLEAN: 62 | if ((boolean) value) JVMClass.mv.visitInsn(ICONST_1); 63 | else JVMClass.mv.visitInsn(ICONST_0); 64 | JVMClass.mv.visitVarInsn(ISTORE, variableIndex); 65 | break; 66 | case CHAR: 67 | JVMClass.mv.visitIntInsn(BIPUSH, (int) ((char) value)); // Overcasting? 68 | JVMClass.mv.visitVarInsn(ISTORE, variableIndex); 69 | break; 70 | case LONG: 71 | JVMClass.mv.visitLdcInsn((long) value); 72 | JVMClass.mv.visitVarInsn(LSTORE, variableIndex); 73 | break; 74 | case FLOAT: 75 | JVMClass.mv.visitLdcInsn((float) value); 76 | JVMClass.mv.visitVarInsn(FSTORE, variableIndex); 77 | break; 78 | case DOUBLE: 79 | JVMClass.mv.visitLdcInsn((double) value); 80 | JVMClass.mv.visitVarInsn(DSTORE, variableIndex); 81 | break; 82 | default: 83 | break; 84 | } 85 | } 86 | 87 | public void changeField(String clazz, Variable v, Object value) { 88 | v.setValue(value); // just in case 89 | JVMClass.mv.visitVarInsn(ALOAD, 0); 90 | switch (v.getType()) { 91 | case INT: 92 | if (Math.abs((int) value) >= 128) JVMClass.mv.visitIntInsn(SIPUSH, (int) value); 93 | else JVMClass.mv.visitIntInsn(BIPUSH, (int) value); 94 | break; 95 | case STRING: 96 | JVMClass.mv.visitLdcInsn(value.toString()); 97 | break; 98 | case BOOLEAN: 99 | if ((boolean) value) JVMClass.mv.visitInsn(ICONST_1); 100 | else JVMClass.mv.visitInsn(ICONST_0); 101 | break; 102 | case CHAR: 103 | JVMClass.mv.visitIntInsn(BIPUSH, (int) ((char) value)); // Overcasting? 104 | break; 105 | case LONG: 106 | JVMClass.mv.visitLdcInsn((long) value); 107 | break; 108 | case FLOAT: 109 | JVMClass.mv.visitLdcInsn((float) value); 110 | break; 111 | case DOUBLE: 112 | JVMClass.mv.visitLdcInsn((double) value); 113 | break; 114 | default: 115 | break; 116 | } 117 | JVMClass.mv.visitFieldInsn(PUTFIELD, clazz, v.getIdentifier(), v.getType().toString()); 118 | } 119 | 120 | protected void build() { 121 | final int variableCount = variables.size(); 122 | JVMClass.mv.visitInsn(RETURN); 123 | int argumentCount = description.split("\\(")[1].split("\\)")[0].length(); 124 | JVMClass.mv.visitMaxs(variableCount > 0 ? 1 : 0, variableCount + argumentCount); 125 | JVMClass.mv.visitEnd(); 126 | JVMClass.mv = null; 127 | } 128 | } -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/Modifiers.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | import static org.objectweb.asm.Opcodes.*; 4 | 5 | public enum Modifiers { 6 | PUBLIC(ACC_PUBLIC), PRIVATE(ACC_PRIVATE), STATIC(ACC_STATIC); 7 | int m; 8 | 9 | private Modifiers(int m) { 10 | this.m = m; 11 | } 12 | 13 | public int toACC() { 14 | return m; 15 | } 16 | 17 | public static Modifiers from(String s) { 18 | if (s.equalsIgnoreCase("static")) return STATIC; 19 | else if (s.equalsIgnoreCase("public")) return PUBLIC; 20 | else if (s.equalsIgnoreCase("private")) return PRIVATE; 21 | else return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/Variable.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg; 2 | 3 | public class Variable { 4 | private String identifier; 5 | private Object value; 6 | private IdentifierType type; 7 | 8 | public Variable(IdentifierType type, String identifier, Object value) { 9 | this.identifier = identifier; 10 | this.type = type; 11 | this.value = value; 12 | } 13 | 14 | public String getIdentifier() { 15 | return identifier; 16 | } 17 | 18 | public IdentifierType getType() { 19 | return type; 20 | } 21 | 22 | public Object getValue() { 23 | return value; 24 | } 25 | 26 | public void setValue(Object v) { 27 | this.value = v; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/xyz/jadonfowler/jvmbg/test/BasicClass.java: -------------------------------------------------------------------------------- 1 | package xyz.jadonfowler.jvmbg.test; 2 | 3 | public class BasicClass { 4 | 5 | int field = 8; 6 | 7 | public void test() { 8 | field = 7; 9 | } 10 | 11 | } 12 | --------------------------------------------------------------------------------