From 22a693097376f7b44a2dde7eae43bd6ea30215ec Mon Sep 17 00:00:00 2001 From: Sander Hautvast Date: Mon, 18 Jan 2021 20:42:24 +0100 Subject: [PATCH] first commit i a long while. Should have committed earlier, but tests were failing. * updated bejava lang * bug fixes --- README.md | 8 +- pom.xml | 8 +- src/main/java/nl/sander/beejava/Compiler.java | 115 +++++---- .../beejava/ConstantPoolEntryCreator.java | 134 +++------- .../java/nl/sander/beejava/FieldWrapper.java | 51 ++++ .../nl/sander/beejava/JavaInstruction.java | 81 ++++++ .../java/nl/sander/beejava/JavaOpcode.java | 12 + ...r.java => MethodCodeAttributeCreator.java} | 16 +- .../java/nl/sander/beejava/OpcodeMapper.java | 55 ---- .../nl/sander/beejava/OpcodeTranslator.java | 214 ++++++++++++++++ .../nl/sander/beejava/OverallCompiler.java | 17 ++ .../java/nl/sander/beejava/Primitive.java | 14 ++ .../beejava/{apiv2 => }/SourceCompiler.java | 116 ++++++--- .../java/nl/sander/beejava/TypeMapper.java | 3 +- .../nl/sander/beejava/api/BeeConstructor.java | 44 +--- .../java/nl/sander/beejava/api/BeeField.java | 47 ++-- .../java/nl/sander/beejava/api/BeeMethod.java | 62 ++--- .../nl/sander/beejava/api/BeePackage.java | 17 -- .../nl/sander/beejava/api/BeeParameter.java | 12 +- .../java/nl/sander/beejava/api/BeeSource.java | 152 +++-------- .../{apiv2 => api}/ClassInstruction.java | 2 +- .../{apiv2 => api}/ClassOperation.java | 4 +- .../nl/sander/beejava/api/CodeContainer.java | 31 ++- .../java/nl/sander/beejava/api/CodeLine.java | 235 +----------------- .../beejava/{apiv2 => api}/Instruction.java | 2 +- .../java/nl/sander/beejava/api/Opcode.java | 124 +++++---- src/main/java/nl/sander/beejava/api/Ref.java | 9 - .../java/nl/sander/beejava/api/Version.java | 2 +- .../sander/beejava/apiv2/BeeConstructor.java | 53 ---- .../nl/sander/beejava/apiv2/BeeField.java | 82 ------ .../nl/sander/beejava/apiv2/BeeMethod.java | 61 ----- .../nl/sander/beejava/apiv2/BeeParameter.java | 37 --- .../nl/sander/beejava/apiv2/BeeSource.java | 110 -------- .../sander/beejava/apiv2/CodeContainer.java | 67 ----- .../nl/sander/beejava/apiv2/CodeLine.java | 28 --- .../java/nl/sander/beejava/apiv2/Opcode.java | 65 ----- .../nl/sander/beejava/classinfo/Info.java | 2 +- .../constantpool/entry/ConstantPoolEntry.java | 25 +- .../constantpool/entry/FloatEntry.java | 6 +- .../constantpool/entry/IntegerEntry.java | 8 +- .../beejava/operands/ConstantOperand.java | 22 ++ .../sander/beejava/operands/FieldOperand.java | 11 + .../operands/LocalVariableOperand.java | 9 + .../beejava/operands/MethodOperand.java | 22 ++ .../nl/sander/beejava/operands/Operand.java | 4 + .../sander/beejava/operands/VoidOperand.java | 9 + .../beejava/BytecodeGeneratorTests.java | 2 +- .../nl/sander/beejava/SourceCompilerTest.java | 16 +- src/test/java/nl/sander/beejava/TestData.java | 127 ++-------- .../java/nl/sander/beejava/TestData2.java | 6 +- .../nl/sander/beejava/TypeMapperTest.java | 22 +- .../entry/TagCorrectnessTest.java | 2 +- .../beejava/e2e/BeanWithMethodsTest.java | 38 ++- .../nl/sander/beejava/e2e/EmptyBeanTest.java | 51 +--- 54 files changed, 966 insertions(+), 1506 deletions(-) create mode 100644 src/main/java/nl/sander/beejava/FieldWrapper.java create mode 100644 src/main/java/nl/sander/beejava/JavaInstruction.java rename src/main/java/nl/sander/beejava/{MethodCodeCreator.java => MethodCodeAttributeCreator.java} (76%) delete mode 100644 src/main/java/nl/sander/beejava/OpcodeMapper.java create mode 100644 src/main/java/nl/sander/beejava/OpcodeTranslator.java create mode 100644 src/main/java/nl/sander/beejava/OverallCompiler.java create mode 100644 src/main/java/nl/sander/beejava/Primitive.java rename src/main/java/nl/sander/beejava/{apiv2 => }/SourceCompiler.java (67%) delete mode 100644 src/main/java/nl/sander/beejava/api/BeePackage.java rename src/main/java/nl/sander/beejava/{apiv2 => api}/ClassInstruction.java (92%) rename src/main/java/nl/sander/beejava/{apiv2 => api}/ClassOperation.java (80%) rename src/main/java/nl/sander/beejava/{apiv2 => api}/Instruction.java (81%) delete mode 100644 src/main/java/nl/sander/beejava/api/Ref.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/BeeConstructor.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/BeeField.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/BeeMethod.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/BeeParameter.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/BeeSource.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/CodeContainer.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/CodeLine.java delete mode 100644 src/main/java/nl/sander/beejava/apiv2/Opcode.java create mode 100644 src/main/java/nl/sander/beejava/operands/ConstantOperand.java create mode 100644 src/main/java/nl/sander/beejava/operands/FieldOperand.java create mode 100644 src/main/java/nl/sander/beejava/operands/LocalVariableOperand.java create mode 100644 src/main/java/nl/sander/beejava/operands/MethodOperand.java create mode 100644 src/main/java/nl/sander/beejava/operands/Operand.java create mode 100644 src/main/java/nl/sander/beejava/operands/VoidOperand.java diff --git a/README.md b/README.md index c3fe852..914a57f 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# beejava -Beejava is a code creation library, somewhat comparable to javassist or bytebuddy. -* It let's you compile java 'opcode' to bytecode. +# bejava +Bejava (backend java compiler) is a code creation library, somewhat comparable to javassist or bytebuddy. +* It let's you compile bejava-lang' to bytecode. * It does not inspect or enhance existing bytecode, though it could be part of such functionality. -What is 'opcode'? +What is 'bejava lang'? The goal of the project is to let developers dynamically create classes, using a simplified version of standard java opcodes. For instance: instead of having to choose between: diff --git a/pom.xml b/pom.xml index d984146..6c3b898 100644 --- a/pom.xml +++ b/pom.xml @@ -2,21 +2,21 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - beejava + bejava org.apache.maven.plugins maven-compiler-plugin + 3.8.0 - 15 - 15 + 15 nl.sander - beejava + bejava 0.1-SNAPSHOT jar diff --git a/src/main/java/nl/sander/beejava/Compiler.java b/src/main/java/nl/sander/beejava/Compiler.java index 59fb1c5..c34ad5f 100644 --- a/src/main/java/nl/sander/beejava/Compiler.java +++ b/src/main/java/nl/sander/beejava/Compiler.java @@ -1,14 +1,11 @@ package nl.sander.beejava; -import nl.sander.beejava.api.BeeMethod; -import nl.sander.beejava.api.BeeSource; -import nl.sander.beejava.api.CodeContainer; -import nl.sander.beejava.api.CodeLine; +import nl.sander.beejava.api.*; +import nl.sander.beejava.classinfo.FieldInfo; import nl.sander.beejava.classinfo.MethodInfo; import nl.sander.beejava.constantpool.ConstantPool; -import nl.sander.beejava.constantpool.entry.ConstantPoolEntry; +import nl.sander.beejava.constantpool.entry.ClassEntry; import nl.sander.beejava.constantpool.entry.FieldRefEntry; -import nl.sander.beejava.constantpool.entry.MethodRefEntry; import nl.sander.beejava.constantpool.entry.Utf8Entry; /** @@ -22,7 +19,6 @@ import nl.sander.beejava.constantpool.entry.Utf8Entry; */ public class Compiler { private final CompiledClass compiledClass; - private final ConstantPoolEntryCreator constantPoolEntryCreator; private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator(); private Utf8Entry codeAttributeNameEntry; @@ -34,7 +30,6 @@ public class Compiler { */ public Compiler(CompiledClass compiledClass) { this.compiledClass = compiledClass; - this.constantPoolEntryCreator = new ConstantPoolEntryCreator(compiledClass); } /** @@ -51,26 +46,57 @@ public class Compiler { * construct a CompiledClass object that contains all information for generating the bytecode */ public CompiledClass compile() { - compiledClass.setConstantPool(createConstantPool()); + compiledClass.getSource().getFields().forEach(this::addToConstantPool); + compiledClass.getSource().getConstructors().forEach(this::addToConstantPool); + compiledClass.getSource().getMethods().forEach(this::addToConstantPool); // compiledClass.getSource() ? + + addClassAndSuperClassEntriesToCompiledClass(); + + ConstantPool constantPool = constantPoolCreator.createConstantPool(compiledClass.getConstantTree()); + + compiledClass.setConstantPool(constantPool); + + // add refs to super interfaces to class descriptor and constantpool + compiledClass.getSource().getInterfaces().forEach(interfase -> { + ClassEntry interfaceEntry = ConstantPoolEntryCreator.getOrCreateClassEntry(interfase); + compiledClass.addInterface(interfaceEntry); + compiledClass.addConstantPoolEntry(interfaceEntry); + }); + + // add refs to fields to class descriptor + compiledClass.getSource().getFields().stream() + .map(field -> + new FieldInfo(ConstantPoolEntryCreator.getOrCreateUtf8(field.getName()), + ConstantPoolEntryCreator.getOrCreateUtf8(internalName(field.getType().getName()))) + .addAccessFlags(field.getAccessFlags()) + ) + .forEach(compiledClass::addField); - constantPoolEntryCreator.addInterfaces(); - constantPoolEntryCreator.addFields(); addConstructors(); addMethods(); return compiledClass; } - private ConstantPool createConstantPool() { - compiledClass.getSource().getConstructors().forEach(this::updateConstantPool); - compiledClass.getSource().getMethods().forEach(this::updateConstantPool); // compiledClass.getSource() ? + // why not put this everywhere, it's not like it's ever going to change + private String internalName(String name) { + return name.replaceAll("\\.", "/"); + } - compiledClass.setThisClass(constantPoolEntryCreator.addThisClass()); - this.codeAttributeNameEntry = constantPoolEntryCreator.getOrCreateUtf8("Code"); + + private void addClassAndSuperClassEntriesToCompiledClass() { + ClassEntry classEntry = ConstantPoolEntryCreator.getOrCreateClassEntry(compiledClass.getSource().getName()); + compiledClass.setThisClass(classEntry); + compiledClass.addConstantPoolEntry(classEntry); + + ClassEntry superClass = ConstantPoolEntryCreator.getOrCreateClassEntry(compiledClass.getSource().getSuperClass()); + compiledClass.addConstantPoolEntry(superClass); + compiledClass.setSuperClass(superClass); + + + this.codeAttributeNameEntry = ConstantPoolEntryCreator.getOrCreateUtf8("Code"); compiledClass.addConstantPoolEntry(codeAttributeNameEntry); - - return constantPoolCreator.createConstantPool(compiledClass.getConstantTree()); } public void addConstructors() { @@ -79,8 +105,7 @@ public class Compiler { .forEach(compiledClass::addMethod); } - - /* + /** * maps methods from the source to a MethodInfo and adds that to the CompiledClass. */ public void addMethods() { @@ -88,34 +113,34 @@ public class Compiler { for (BeeMethod method : compiledClass.getSource().getMethods()) { compiledClass.addMethod(createMethod(method)); } - -// compiledClass.getSource().getMethods().stream() -// .map(this::createMethod) -// .forEach(compiledClass::addMethod); } - /* - * create bytecode for method + /** * create methodInfo object for classfile */ private MethodInfo createMethod(CodeContainer method) { return new MethodInfo( - constantPoolEntryCreator.getOrCreateUtf8(method.getName()), - constantPoolEntryCreator.getOrCreateUtf8(method.getSignature())) + ConstantPoolEntryCreator.getOrCreateUtf8(method.getName()), + ConstantPoolEntryCreator.getOrCreateUtf8(method.getSignature())) .addAccessFlags(method.getAccessFlags()) - .addAttribute(MethodCodeCreator.createCodeAttribute(codeAttributeNameEntry, method)); + .addAttribute(MethodCodeAttributeCreator.createCodeAttribute(codeAttributeNameEntry, method)); } - /* + private void addToConstantPool(BeeField field) { + FieldRefEntry fieldRefEntry = ConstantPoolEntryCreator.getOrCreateFieldRefEntry(compiledClass.getSource().getName(), field.getName(), field.getType()); + compiledClass.addConstantPoolEntry(fieldRefEntry); + } + + /** * inspect a method or constructor, extract items that need to be added, and add them to the constant pool */ - private void updateConstantPool(CodeContainer codeContainer) { - compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateUtf8(codeContainer.getName())); - compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateUtf8(codeContainer.getSignature())); - codeContainer.getCode().forEach(this::updateConstantPool); + private void addToConstantPool(CodeContainer codeContainer) { + compiledClass.addConstantPoolEntry(ConstantPoolEntryCreator.getOrCreateUtf8(codeContainer.getName())); + compiledClass.addConstantPoolEntry(ConstantPoolEntryCreator.getOrCreateUtf8(codeContainer.getSignature())); + codeContainer.getExpandedCode().forEach(this::updateConstantPool); } - /* + /** * Scan code line for items that need adding to the constant pool * * Constantpool uniqueness is maintained in two ways: @@ -124,23 +149,17 @@ public class Compiler { * 2. the root node (also in aforementioned cache) is not symmetric because it is the only one that can be added to The compiledClass. * So Compiled class also contains a set of unique root nodes. */ - private void updateConstantPool(CodeLine codeline) { - if (codeline.hasMethodCall()) { - MethodRefEntry methodRefEntry = constantPoolEntryCreator.getOrCreateMethodRefEntry(codeline); - codeline.setAssignedEntry(methodRefEntry); - compiledClass.addConstantPoolEntry(methodRefEntry); + private void updateConstantPool(JavaInstruction instruction) { + if (instruction.hasMethodRef()) { + compiledClass.addConstantPoolEntry(instruction.getMethodRef()); } - if (codeline.hasRefToOwnField() || codeline.hasRefToExternalField()) { - FieldRefEntry fieldRefEntry = constantPoolEntryCreator.getOrCreateFieldRefEntry(codeline); - codeline.setAssignedEntry(fieldRefEntry); - compiledClass.addConstantPoolEntry(fieldRefEntry); + if (instruction.hasFieldRef()) { + compiledClass.addConstantPoolEntry(instruction.getFieldRef()); } - if (codeline.hasConstValue()) { - ConstantPoolEntry primitiveEntry = constantPoolEntryCreator.getOrCreatePrimitiveEntry(codeline); - codeline.setAssignedEntry(primitiveEntry); - compiledClass.addConstantPoolEntry(primitiveEntry); + if (instruction.hasConstantRef()) { + compiledClass.addConstantPoolEntry(instruction.getConstantEntry()); } } } diff --git a/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java b/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java index 53e746d..2edafd7 100644 --- a/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java +++ b/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java @@ -1,10 +1,7 @@ package nl.sander.beejava; -import nl.sander.beejava.api.CodeLine; -import nl.sander.beejava.api.Ref; -import nl.sander.beejava.classinfo.FieldInfo; -import nl.sander.beejava.classinfo.attributes.CodeAttribute; import nl.sander.beejava.constantpool.entry.*; +import nl.sander.beejava.operands.ConstantOperand; import java.util.HashMap; import java.util.Map; @@ -14,147 +11,84 @@ import java.util.Map; * Responsible for creating unique constant pool entries */ class ConstantPoolEntryCreator { - private final Map cache = new HashMap<>(); - private final CompiledClass compiledClass; - public ConstantPoolEntryCreator(CompiledClass compiledClass) { - this.compiledClass = compiledClass; + private static final Map cache = new HashMap<>(); + + private ConstantPoolEntryCreator() { } /* * creates a FieldRefEntry when not found in cache, otherwise gets it from there */ - FieldRefEntry getOrCreateFieldRefEntry(CodeLine codeLine) { - return cache(new FieldRefEntry(getOrCreateClassEntry(codeLine), getOrCreateFieldNameAndType(codeLine))); + static FieldRefEntry getOrCreateFieldRefEntry(String declaringClassName, String fieldName, Class fieldType) { + return cache(new FieldRefEntry(getOrCreateClassEntry(declaringClassName), getOrCreateFieldNameAndType(fieldName, fieldType))); } /* * creates a MethodRefEntry when not found in cache, otherwise gets it from there */ - MethodRefEntry getOrCreateMethodRefEntry(CodeLine codeline) { - ClassEntry classEntry = getOrCreateClassEntry(codeline); - return cache(new MethodRefEntry(classEntry, getOrCreateMethodNameAndType(codeline))); + static MethodRefEntry getOrCreateMethodRefEntry(String className, String methodName, String signature) { + ClassEntry classEntry = getOrCreateClassEntry(className); + return cache(new MethodRefEntry(classEntry, getOrCreateMethodNameAndType(methodName, signature))); } /* * creates a NamAndTypeEntry for a field when not found in cache, otherwise gets it from there */ - private NameAndTypeEntry getOrCreateFieldNameAndType(CodeLine codeline) { - if (codeline.hasRefToOwnField()) { - return cache(new NameAndTypeEntry( - cache(new Utf8Entry(codeline.getOwnfield().getName())), - cache(new Utf8Entry(TypeMapper.map(codeline.getOwnfield().getType()))))); // is actually a shortcut - } else {//TODO this method may need some work - return cache(new NameAndTypeEntry( - cache(new Utf8Entry(codeline.getExternalfield().getName())), - cache(new Utf8Entry(TypeMapper.map(codeline.getExternalfield().getType()))) - )); - } + private static NameAndTypeEntry getOrCreateFieldNameAndType(String name, Class type) { + return cache(new NameAndTypeEntry( + cache(new Utf8Entry(name)), + cache(new Utf8Entry(TypeMapper.map(type))))); } /* * creates a NamAndTypeEntry for a method when not found in cache, otherwise gets it from there */ - private NameAndTypeEntry getOrCreateMethodNameAndType(CodeLine codeline) { + private static NameAndTypeEntry getOrCreateMethodNameAndType(String methodName, String methodSignature) { return new NameAndTypeEntry( - cache(new Utf8Entry(codeline.getMethodName())), - cache(new Utf8Entry(codeline.getMethodSignature()))); - } - - /* - * creates a ClassEntry when not found in cache, otherwise gets it from there - */ - private ClassEntry getOrCreateClassEntry(CodeLine codeline) { - if (codeline.hasRef()) { - if (codeline.getRef() == Ref.SUPER) { // this and super are rather special - ClassEntry superClass = getClassEntry(compiledClass.getSource().getSuperClass().getName()); - compiledClass.setSuperClass(superClass); - return superClass; - - } else if (codeline.getRef() == Ref.THIS) { - return addThisClass(); - } - } else if (codeline.hasClassName()) { - return getClassEntry(codeline.getClassName()); - } - - throw new RuntimeException("shouldn't be here"); - } - - /* - * this method gives me a headache. It adds, but also creates a classEntry for the this class - */ - ClassEntry addThisClass() { - ClassEntry classEntry = getClassEntry(compiledClass.getSource().getName()); - compiledClass.addConstantPoolEntry(classEntry); - return classEntry; + cache(new Utf8Entry(methodName)), + cache(new Utf8Entry(methodSignature))); } /* * get or create a ClassEntry */ - private ClassEntry getClassEntry(String externalClassName) { + public static ClassEntry getOrCreateClassEntry(String externalClassName) { return cache(new ClassEntry(cache(new Utf8Entry(internalName(externalClassName))))); } - /* - * Adds interfaces to the constant pool as well as the class. - * - * interfaces[] in the class file is an array of cp entries - */ - public void addInterfaces() { - compiledClass.getSource().getInterfaces().forEach(interfase -> { - ClassEntry interfaceEntry = cache(new ClassEntry(cache(new Utf8Entry(internalName(interfase.getName()))))); - compiledClass.addInterface(interfaceEntry); - compiledClass.addConstantPoolEntry(interfaceEntry); - }); + public static ClassEntry getOrCreateClassEntry(Class type) { + return getOrCreateClassEntry(type.getName()); } /* * If a constant is in the codeline, it needs to be added to the constant pool. */ - public ConstantPoolEntry getOrCreatePrimitiveEntry(CodeLine codeline) { - Object v = codeline.getConstValue(); - - if (v instanceof String) { - return cache(new StringEntry(cache(new Utf8Entry((String) v)))); - } else if (v instanceof Integer) { - return cache(new IntegerEntry((Integer) v)); - } else if (v instanceof Long) { - return cache(new LongEntry((Long) v)); - } else if (v instanceof Float) { - return cache(new FloatEntry((Float) v)); - } else if (v instanceof Double) { - return cache(new DoubleEntry((Double) v)); - } - throw new IllegalStateException("shouldn't be here"); // TODO find out why are you here + public static ConstantPoolEntry getOrCreatePrimitiveConstantEntry(ConstantOperand operand) { + return cache(switch (operand.getType()) { + case STRING -> new StringEntry(cache(new Utf8Entry(operand.getValue()))); + case INT, BYTE, SHORT -> new IntegerEntry(operand.getValue()); + case LONG -> new LongEntry(Long.parseLong(operand.getValue())); + case FLOAT -> new FloatEntry(operand.getValue()); + case DOUBLE -> new DoubleEntry(Double.parseDouble(operand.getValue())); + case CHAR -> new IntegerEntry(Character.codePointAt(operand.getValue(), 0)); + case BOOLEAN -> new IntegerEntry(Boolean.parseBoolean(operand.getValue()) ? 1 : 0); + }); } - /* - * maps a field from the source to a FieldInfo and adds that to the CompiledClass. - */ - public void addFields() { - compiledClass.getSource().getFields().stream() - .map(field -> new FieldInfo( - cache(new Utf8Entry(field.getName())), - cache(new Utf8Entry(internalName(field.getType().getName()))) - ).addAccessFlags(field.getAccessFlags()) - ) - .forEach(compiledClass::addField); - } - - public Utf8Entry getOrCreateUtf8(String utf8) { + public static Utf8Entry getOrCreateUtf8(String utf8) { return cache(new Utf8Entry(utf8)); } // why not put this everywhere, it's not like it's ever going to change - private String internalName(String name) { + private static String internalName(String name) { return name.replaceAll("\\.", "/"); } @SuppressWarnings("unchecked") - T cache(T newEntry) { - // First create an object using the supplier, but if it's found in cache, return the cached entry and discard the first. + static T cache(T newEntry) { + // if the argument newEntry is found in cache, return the cached entry and discard the argument + // Otherwise store it. // Can't check for equality unless you create a potential new entry first int hash = newEntry.hashCode(); return (T) cache.computeIfAbsent(hash, k -> newEntry); diff --git a/src/main/java/nl/sander/beejava/FieldWrapper.java b/src/main/java/nl/sander/beejava/FieldWrapper.java new file mode 100644 index 0000000..d79ce43 --- /dev/null +++ b/src/main/java/nl/sander/beejava/FieldWrapper.java @@ -0,0 +1,51 @@ +package nl.sander.beejava; + +import nl.sander.beejava.api.BeeField; +import nl.sander.beejava.flags.AccessFlags; + +import java.lang.reflect.Field; + +/** + * Union type for an existing standard java field (already loaded) or a field for the class that is being compiled + */ +public class FieldWrapper { + private final Field reflectField; + private final BeeField beefield; + + public FieldWrapper(Field reflectField, BeeField beefield) { + this.reflectField = reflectField; + this.beefield = beefield; + } + + public Class getType() { + if (reflectField != null) { + return reflectField.getType(); + } else { + return beefield.getType(); + } + } + + public String getName() { + if (reflectField != null) { + return reflectField.getName(); + } else { + return beefield.getName(); + } + } + + public int getModifiers() { + if (reflectField != null) { + return reflectField.getModifiers(); + } else { + return AccessFlags.combine(beefield.getAccessFlags()); + } + } + + public String getOwner() { + if (reflectField != null) { + return reflectField.getDeclaringClass().getName(); + } else { + return beefield.getDeclaringClass(); + } + } +} diff --git a/src/main/java/nl/sander/beejava/JavaInstruction.java b/src/main/java/nl/sander/beejava/JavaInstruction.java new file mode 100644 index 0000000..f7ac201 --- /dev/null +++ b/src/main/java/nl/sander/beejava/JavaInstruction.java @@ -0,0 +1,81 @@ +package nl.sander.beejava; + +import nl.sander.beejava.constantpool.entry.ConstantPoolEntry; +import nl.sander.beejava.constantpool.entry.FieldRefEntry; +import nl.sander.beejava.constantpool.entry.MethodRefEntry; + +public class JavaInstruction { + private final JavaOpcode opcode; + private final MethodRefEntry methodRefEntry; + private final FieldRefEntry fieldRefEntry; + private final ConstantPoolEntry constantEntry; + private Integer localVariableIndex; + + public JavaInstruction(JavaOpcode opcode) { + this(opcode, null, null, null, null); + } + + public JavaInstruction(JavaOpcode opcode, int localVariableIndex) { + this(opcode, localVariableIndex, null, null, null); + } + + public JavaInstruction(JavaOpcode opcode, ConstantPoolEntry constantEntry) { + this(opcode, null, null, null, constantEntry); + } + + public JavaInstruction(JavaOpcode opcode, MethodRefEntry methodRefEntry) { + this(opcode, null, methodRefEntry, null, null); + } + + public JavaInstruction(JavaOpcode opcode, FieldRefEntry fieldRefEntry) { + this(opcode, null, null, fieldRefEntry, null); + } + + private JavaInstruction(JavaOpcode opcode, Integer localVariableIndex, MethodRefEntry methodRefEntry, FieldRefEntry fieldRefEntry, ConstantPoolEntry constantEntry) { + this.opcode = opcode; + this.localVariableIndex = localVariableIndex; + this.methodRefEntry = methodRefEntry; + this.fieldRefEntry = fieldRefEntry; + this.constantEntry = constantEntry; + } + + public JavaOpcode getOpcode() { + return opcode; + } + + public MethodRefEntry getMethodRef() { + return methodRefEntry; + } + + public FieldRefEntry getFieldRef() { + return fieldRefEntry; + } + + public boolean hasMethodRef() { + return methodRefEntry != null; + } + + public boolean hasFieldRef() { + return fieldRefEntry != null; + } + + public ConstantPoolEntry getEntry() { + if (methodRefEntry != null) { + return methodRefEntry; + } else if (fieldRefEntry != null) { + return fieldRefEntry; + } else if (constantEntry!=null){ + return constantEntry; + } else { + return null; // Not sure yet what to do here. + } + } + + public boolean hasConstantRef() { + return constantEntry !=null; + } + + public ConstantPoolEntry getConstantEntry() { + return constantEntry; + } +} diff --git a/src/main/java/nl/sander/beejava/JavaOpcode.java b/src/main/java/nl/sander/beejava/JavaOpcode.java index dc8bdf1..4519907 100644 --- a/src/main/java/nl/sander/beejava/JavaOpcode.java +++ b/src/main/java/nl/sander/beejava/JavaOpcode.java @@ -5,8 +5,20 @@ public enum JavaOpcode { LDC_W(0x13,true, +1), LDC2_W ( 0x14, true, +2), + ALOAD ( 0x19, false, +1), ALOAD_0 ( 0x2a, false, +1), + ALOAD_1 ( 0x2b, false, +1), + ALOAD_2 ( 0x2c, false, +1), + ALOAD_3 ( 0x2d, false, +1), + + IRETURN ( 0xac,false, -1), + LRETURN(0xad, false, -2), + FRETURN(0xae,false,-1), + DRETURN(0xaf,false,-2), + ARETURN ( 0xb0,false, -1), + RETURN ( 0xb1,false, 0), + GETSTATIC ( 0xb2,true, +1), GETFIELD ( 0xb4,true, +1), diff --git a/src/main/java/nl/sander/beejava/MethodCodeCreator.java b/src/main/java/nl/sander/beejava/MethodCodeAttributeCreator.java similarity index 76% rename from src/main/java/nl/sander/beejava/MethodCodeCreator.java rename to src/main/java/nl/sander/beejava/MethodCodeAttributeCreator.java index 5a63edf..91714a2 100644 --- a/src/main/java/nl/sander/beejava/MethodCodeCreator.java +++ b/src/main/java/nl/sander/beejava/MethodCodeAttributeCreator.java @@ -8,7 +8,7 @@ import nl.sander.beejava.constantpool.entry.MethodRefEntry; import nl.sander.beejava.constantpool.entry.Utf8Entry; import nl.sander.beejava.util.ByteBuf; -public class MethodCodeCreator { +public class MethodCodeAttributeCreator { public static CodeAttribute createCodeAttribute(Utf8Entry codeAttributeNameEntry, CodeContainer codeContainer) { CodeAttribute codeAttribute = new CodeAttribute(codeAttributeNameEntry); @@ -16,10 +16,10 @@ public class MethodCodeCreator { codeAttribute.setMaxLocals(codeContainer.getFormalParameters().size() + 1); ByteBuf byteBuf = new ByteBuf(); - codeContainer.getCode().forEach(codeLine -> { - JavaOpcode javaOpcode = codeLine.getJavaOpcode(); // the opcode we determined in calculateMaxStack + codeContainer.getExpandedCode().forEach(instruction -> { + JavaOpcode javaOpcode = instruction.getOpcode(); byteBuf.addU8(javaOpcode.getByteCode()); - ConstantPoolEntry constantPoolEntry = codeLine.getAssignedEntry(); + ConstantPoolEntry constantPoolEntry = instruction.getEntry(); if (constantPoolEntry != null) { if (javaOpcode.isWide()) { byteBuf.addU16(constantPoolEntry.getIndex()); @@ -37,11 +37,9 @@ public class MethodCodeCreator { private static int calculateMaxStack(CodeContainer codeContainer) { int stackSize = 0; int maxStackSize = 0; - for (CodeLine codeLine : codeContainer.getCode()) { - JavaOpcode javaOpcode = OpcodeMapper.mapOpcode(codeLine); - codeLine.setJavaOpcode(javaOpcode); //not really nice that we mutate codeLine, but this way we don't have to calculate the JavaOpcode twice - stackSize += javaOpcode.getStackDif(); - ConstantPoolEntry assignedEntry = codeLine.getAssignedEntry(); + for (JavaInstruction instruction : codeContainer.getExpandedCode()) { + stackSize += instruction.getOpcode().getStackDif(); + ConstantPoolEntry assignedEntry = instruction.getEntry(); if (assignedEntry instanceof MethodRefEntry) { MethodRefEntry methodRefEntry = (MethodRefEntry) assignedEntry; String argumentTypes = methodRefEntry.getNameAndType().getType(); diff --git a/src/main/java/nl/sander/beejava/OpcodeMapper.java b/src/main/java/nl/sander/beejava/OpcodeMapper.java deleted file mode 100644 index 6ca6cb7..0000000 --- a/src/main/java/nl/sander/beejava/OpcodeMapper.java +++ /dev/null @@ -1,55 +0,0 @@ -package nl.sander.beejava; - -import nl.sander.beejava.api.BeeSource; -import nl.sander.beejava.api.CodeLine; -import nl.sander.beejava.api.Opcode; -import nl.sander.beejava.flags.ClassAccessFlags; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; - -import static nl.sander.beejava.JavaOpcode.*; - -public class OpcodeMapper { - - public static JavaOpcode mapOpcode(CodeLine codeLine) { - Opcode opcode = codeLine.getOpcode(); - return switch (opcode) { - case GET -> isStatic(codeLine.getExternalfield()) ? GETSTATIC : GETFIELD; - case LD_VAR -> ALOAD_0; - case LD_CONST -> loadConst(codeLine); - case INVOKE -> invoke(codeLine); - case RETURN -> JavaOpcode.RETURN; //TODO not complete yet - case PUT -> JavaOpcode.PUTFIELD; - default -> throw new IllegalStateException("something not implemented"); - }; - } - - /* TODO cover more cases */ - private static JavaOpcode invoke(CodeLine codeLine) { - BeeSource source = codeLine.getOwner().getOwner(); - if (source.getAccessFlags().contains(ClassAccessFlags.SUPER) && codeLine.getOwner().isConstructor()) { - return INVOKESPECIAL; - } else { - return INVOKEVIRTUAL; - } - } - - private static JavaOpcode loadConst(CodeLine codeLine) { - Object constValue = codeLine.getConstValue(); - int index = codeLine.getAssignedEntry().getIndex(); - if (constValue instanceof Double || constValue instanceof Long) { - return LDC2_W; - } else { - if (index > 0xff) { - return LDC_W; - } else { - return LDC; - } - } - } - - private static boolean isStatic(Field field) { - return (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC; - } -} diff --git a/src/main/java/nl/sander/beejava/OpcodeTranslator.java b/src/main/java/nl/sander/beejava/OpcodeTranslator.java new file mode 100644 index 0000000..c565622 --- /dev/null +++ b/src/main/java/nl/sander/beejava/OpcodeTranslator.java @@ -0,0 +1,214 @@ +package nl.sander.beejava; + +import nl.sander.beejava.api.*; +import nl.sander.beejava.constantpool.entry.ConstantPoolEntry; +import nl.sander.beejava.operands.*; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.stream.Collectors; + +enum Primitives { + INT, SHORT, BYTE, LONG, FLOAT, DOUBLE, CHAR, BOOLEAN; + + public static Primitives from(String value) { + return Arrays.stream(values()).filter(e -> e.toString().equals(value)).findFirst().orElse(null); + } +} + +/** + * Translates from BeeJava opcodes to actual java opcodes. + */ +public class OpcodeTranslator { + private final static OpcodeTranslator instance = new OpcodeTranslator(); + private final Parser parser = new Parser(); + private BeeSource beeSource; + + /* + * trying to make SourceExpander reusable. + * @not_multithreaded. + * //TODO should not mutate beesource + */ + public static void translate(BeeSource beeSource) { + instance.init(beeSource); + instance.doExpand(); + } + + private void init(BeeSource beeSource) { + this.beeSource = beeSource; + } + + private void doExpand() { + beeSource.getConstructors().forEach(this::translate); + beeSource.getMethods().forEach(this::translate); + } + + private void translate(CodeContainer codeContainer) { + var methodInstructions = codeContainer.getCode().stream() + .flatMap(o -> translate(codeContainer, o).stream()) + .collect(Collectors.toList()); + codeContainer.setExpandedCode(methodInstructions); + } + + private List translate(CodeContainer codeContainer, CodeLine codeLine) { + var operand = parser.parse(codeLine); + return eval(codeContainer, codeLine.getOpcode(), operand); + } + + private List eval(CodeContainer codeContainer, Opcode opcode, Operand operand) { + final List instructions = new ArrayList<>(); + if (operand instanceof MethodOperand) { + var mo = (MethodOperand) operand; + if ("this".equals(mo.className)) { + instructions.add(new JavaInstruction(JavaOpcode.ALOAD_0)); + } + } + if (opcode == Opcode.LOAD) { + if (operand instanceof ConstantOperand) { + ConstantPoolEntry constantEntry = ConstantPoolEntryCreator.getOrCreatePrimitiveConstantEntry((ConstantOperand) operand); + instructions.add(new JavaInstruction(JavaOpcode.LDC, constantEntry)); + } + } else if (opcode == Opcode.RETURN && operand instanceof VoidOperand) { + instructions.add(new JavaInstruction(JavaOpcode.RETURN)); + } else if (opcode == Opcode.GET) { + assert operand instanceof FieldOperand; + + var fieldOperand = (FieldOperand) operand; + FieldWrapper field = getField(codeContainer, fieldOperand); + instructions.add(new JavaInstruction(getJavaOpcodeForGet(field), + ConstantPoolEntryCreator.getOrCreateFieldRefEntry(getType(codeContainer, fieldOperand), field.getName(), field.getType()))); + } else if (opcode == Opcode.INVOKE) { + assert operand instanceof MethodOperand; + var mo = (MethodOperand) operand; + if ("super".equals(mo.methodName)) { + instructions.add(new JavaInstruction(JavaOpcode.INVOKESPECIAL, + ConstantPoolEntryCreator.getOrCreateMethodRefEntry(beeSource.getSuperClass().getName(), "", "()V"))); + } else { + instructions.add(new JavaInstruction(JavaOpcode.INVOKEVIRTUAL, ConstantPoolEntryCreator.getOrCreateMethodRefEntry(mo.className, mo.methodName, mo.signature))); + } + System.out.println(operand); + } else if (operand instanceof FieldOperand) { + var fieldOperand = (FieldOperand) operand; + if (fieldOperand.className.equals("this")) { + instructions.add(new JavaInstruction(JavaOpcode.ALOAD_0)); + } + + if (opcode == Opcode.RETURN) { + instructions.add(getReturnInstruction(codeContainer, fieldOperand)); + } + } else if (operand instanceof LocalVariableOperand) { + var localVariable = (LocalVariableOperand) operand; + instructions.add(codeContainer.getParameter(localVariable.name).map(this::getJavaLoadInstruction).orElse(null)); //else case + } + + // TODO continue here. finish simpleBean + return instructions; + } + + private JavaOpcode getJavaOpcodeForGet(FieldWrapper field) { + JavaOpcode get; + if (Modifier.isStatic(field.getModifiers())) { + get = JavaOpcode.GETSTATIC; + } else { + get = JavaOpcode.GETFIELD; + } + return get; + } + + private JavaInstruction getReturnInstruction(CodeContainer codeContainer, FieldOperand fo) { + FieldWrapper field = getField(codeContainer, fo); + + JavaOpcode javaOpcode = switch (field.getName()) { + case "int", "byte", "boolean", "short" -> JavaOpcode.IRETURN; + case "long" -> JavaOpcode.LRETURN; + case "float" -> JavaOpcode.FRETURN; + case "double" -> JavaOpcode.DRETURN; + default -> JavaOpcode.ARETURN; + }; + return new JavaInstruction(javaOpcode); + + } + + private String getType(CodeContainer codeContainer, FieldOperand fo) { + if (fo.className.equals("this")) { + return codeContainer.getName(); + } else { + return fo.className; + } + } + + + private FieldWrapper getField(CodeContainer codeContainer, FieldOperand fo) { + try { + Field javaField = null; + BeeField beeField = null; + if (fo.className.equals("this")) { + beeField = codeContainer.getOwner().getField(fo.fieldName); + } else { + Class type = Class.forName(fo.className); + javaField = type.getDeclaredField(fo.fieldName); + } + return new FieldWrapper(javaField, beeField); + } catch (ClassNotFoundException | NoSuchFieldException e) { + throw new IllegalStateException(e); + } + } + + private JavaInstruction getJavaLoadInstruction(BeeParameter parameter) { + return switch (parameter.getIndex()) { + case 0 -> new JavaInstruction(JavaOpcode.ALOAD_0); + case 1 -> new JavaInstruction(JavaOpcode.ALOAD_1); + case 2 -> new JavaInstruction(JavaOpcode.ALOAD_2); + case 3 -> new JavaInstruction(JavaOpcode.ALOAD_3); + default -> new JavaInstruction(JavaOpcode.ALOAD, parameter.getIndex()); + }; + } +} + +class Parser { + + Operand parse(CodeLine codeLine) { + var operand = codeLine.getOperand(); + if (operand == null) { + return VoidOperand.INSTANCE; + } else { + if (operand.contains("(")) { + var parenIndex = operand.indexOf('('); + var signature = operand.substring(parenIndex); + var classAndMethodeName = operand.substring(0, parenIndex); + int lastDotIndex = classAndMethodeName.lastIndexOf('.'); + var className = classAndMethodeName.substring(0, lastDotIndex); + var methodName = classAndMethodeName.substring(lastDotIndex + 1); + return new MethodOperand(className, methodName, signature); + } else { + int index = operand.lastIndexOf('.'); + if (index < 0) { + var constantOperandOrNull = asConstantOperand(operand); + return Objects.requireNonNullElseGet(constantOperandOrNull, () -> new LocalVariableOperand(operand)); + } else if (index < operand.length() - 1) { + var className = operand.substring(0, index); + var fieldName = operand.substring(index + 1); + return new FieldOperand(className, fieldName); + } + } + } + throw new IllegalArgumentException("compile error"); + } + + ConstantOperand asConstantOperand(String value) { + //TODO add boxed types + var tokens = value.split(" "); + var type = Primitive.from(tokens[0].toUpperCase(Locale.ROOT)); + if (tokens.length == 2 && type != null) { + return new ConstantOperand(type, tokens[1]); + } else if (value.startsWith("\"")) { + if (value.endsWith("\"")) { + return new ConstantOperand(Primitive.STRING, value.substring(1, value.length() - 1)); + } else { + throw new IllegalArgumentException("Unterminated String value"); + } + } + return null; + } +} \ No newline at end of file diff --git a/src/main/java/nl/sander/beejava/OverallCompiler.java b/src/main/java/nl/sander/beejava/OverallCompiler.java new file mode 100644 index 0000000..52c13d8 --- /dev/null +++ b/src/main/java/nl/sander/beejava/OverallCompiler.java @@ -0,0 +1,17 @@ +package nl.sander.beejava; + +import nl.sander.beejava.api.BeeSource; + +public final class OverallCompiler { + + private OverallCompiler(){ + // + } + + public static byte[] compile(String sourcecode){ + BeeSource beeSource = SourceCompiler.compile(sourcecode); + OpcodeTranslator.translate(beeSource); + CompiledClass compiledClass = Compiler.compile(beeSource); + return BytecodeGenerator.generate(compiledClass); + } +} diff --git a/src/main/java/nl/sander/beejava/Primitive.java b/src/main/java/nl/sander/beejava/Primitive.java new file mode 100644 index 0000000..31a2cbf --- /dev/null +++ b/src/main/java/nl/sander/beejava/Primitive.java @@ -0,0 +1,14 @@ +package nl.sander.beejava; + +import java.util.Arrays; + +public enum Primitive { + INT, SHORT, BYTE, LONG, FLOAT, DOUBLE, CHAR, BOOLEAN, STRING; + + public static Primitive from(String value) { + return Arrays.stream(values()) + .filter(e -> e.toString().equals(value)) + .findFirst() + .orElse(null); + } +} \ No newline at end of file diff --git a/src/main/java/nl/sander/beejava/apiv2/SourceCompiler.java b/src/main/java/nl/sander/beejava/SourceCompiler.java similarity index 67% rename from src/main/java/nl/sander/beejava/apiv2/SourceCompiler.java rename to src/main/java/nl/sander/beejava/SourceCompiler.java index a75ff1f..683e1c8 100644 --- a/src/main/java/nl/sander/beejava/apiv2/SourceCompiler.java +++ b/src/main/java/nl/sander/beejava/SourceCompiler.java @@ -1,6 +1,6 @@ -package nl.sander.beejava.apiv2; +package nl.sander.beejava; -import nl.sander.beejava.api.Version; +import nl.sander.beejava.api.*; import nl.sander.beejava.flags.ClassAccessFlags; import nl.sander.beejava.flags.FieldAccessFlag; import nl.sander.beejava.flags.MethodAccessFlag; @@ -30,9 +30,11 @@ public class SourceCompiler { } public BeeSource doCompile() { - Arrays.stream(sourcecode.split("\n")).map(this::compileLine).forEach(instructions::add); + Arrays.stream(sourcecode.split("\n")) + .map(this::compileLine) + .forEach(instructions::add); - BeeSource beeSource = new nl.sander.beejava.apiv2.BeeSource(); + BeeSource beeSource = new BeeSource(); for (currentLine = 0; currentLine < instructions.size(); ) { Instruction ins = instructions.get(currentLine); @@ -51,15 +53,15 @@ public class SourceCompiler { ClassInstruction classInstruction = (ClassInstruction) instruction; String operand = classInstruction.getOperand(); switch (classInstruction.getOperation()) { - case FIELD -> beeSource.addFields(parseField(operand)); - case CONSTRUCTOR -> beeSource.addConstructors(parseConstructor(operand)); - case METHOD -> beeSource.addMethods(parseMethod(operand)); + case FIELD -> beeSource.addFields(parseField(beeSource, operand)); + case CONSTRUCTOR -> beeSource.addConstructors(parseConstructor(beeSource, operand)); + case METHOD -> beeSource.addMethods(parseMethod(beeSource, operand)); default -> throw new IllegalArgumentException("Not allowed here"); } } } - private BeeMethod parseMethod(String text) { + private nl.sander.beejava.api.BeeMethod parseMethod(BeeSource beeSource, String text) { String[] tokens = returntypesplitter.split(text); final String first; final Class returnType; @@ -83,42 +85,41 @@ public class SourceCompiler { } String[] nameParams = split(flagsNameParameters[i], parensplitter); - Set parameters = new HashSet<>(); - String methodName=null; + Set parameters = new HashSet<>(); + String methodName = null; if (nameParams.length > 0) { methodName = nameParams[0]; if (nameParams[1].length() > 0) { - + int index = 0; String params = nameParams[1]; String[] paramTokens = params.split(","); for (String paramToken : paramTokens) { String[] declaration = paramToken.trim().split(" "); String type = declaration[0]; String name = declaration[1]; - parameters.add(new BeeParameter(getType(type), name)); + parameters.add(new nl.sander.beejava.api.BeeParameter(getType(type), name, index++)); } } } - if (methodName==null){ + if (methodName == null) { throw new IllegalArgumentException("method name not found"); } currentLine += 1; - List lines = new ArrayList<>(); + List lines = new ArrayList<>(); Instruction nextInstruction = instructions.get(currentLine); - while (nextInstruction instanceof CodeLine) { - lines.add((CodeLine) nextInstruction); - currentLine += 1; - if (currentLine >= instructions.size()) { - break; // too tired to think - } + while (currentLine < instructions.size() && nextInstruction instanceof CodeLine) { nextInstruction = instructions.get(currentLine); + if (nextInstruction instanceof CodeLine) { + lines.add((CodeLine) nextInstruction); + currentLine += 1; + } } - return new BeeMethod(methodName, flags, parameters, returnType, lines); + return new BeeMethod(beeSource, methodName, flags, parameters, returnType, lines); } - private BeeConstructor parseConstructor(String text) { + private nl.sander.beejava.api.BeeConstructor parseConstructor(BeeSource beeSource, String text) { String[] tokens = split(text, parensplitter); String flag = tokens[0]; MethodAccessFlag methodAccessFlag = MethodAccessFlag.get(flag.toUpperCase()).orElseThrow(illegalArgument("Not a valid flag " + flag)); @@ -127,30 +128,29 @@ public class SourceCompiler { Set parameters = new HashSet<>(); if (params.length() > 0) { String[] paramTokens = params.split(","); + int index = 0; for (String paramToken : paramTokens) { String[] declaration = paramToken.trim().split(" "); String type = declaration[0]; String name = declaration[1]; - try { - parameters.add(new BeeParameter(Class.forName(type), name)); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("field type " + type + " not found"); - } + parameters.add(new BeeParameter(getType(type), name, index++)); } } currentLine += 1; List lines = new ArrayList<>(); Instruction nextInstruction = instructions.get(currentLine); - while (nextInstruction instanceof CodeLine) { - lines.add((CodeLine) nextInstruction); - currentLine += 1; + while (currentLine < instructions.size() && nextInstruction instanceof CodeLine) { nextInstruction = instructions.get(currentLine); - + if (nextInstruction instanceof CodeLine) { + lines.add((CodeLine) nextInstruction); + currentLine += 1; + } } - return new BeeConstructor(Set.of(methodAccessFlag), parameters, lines); + + return new BeeConstructor(beeSource, Set.of(methodAccessFlag), parameters, lines); } - private BeeField parseField(String operand) { + private nl.sander.beejava.api.BeeField parseField(BeeSource beeSource, String operand) { String[] tokens = operand.split(" "); Set flags = new HashSet<>(); String type = null; @@ -168,19 +168,29 @@ public class SourceCompiler { } } currentLine += 1; - return new BeeField(flags, getType(type), name); + return new BeeField(beeSource.getName(), flags, getType(type), name); } - private void parseClassDeclaration(Instruction firstLine, BeeSource beeSource) { - if (firstLine instanceof ClassInstruction) { - ClassInstruction classDeclaration = (ClassInstruction) firstLine; + private void parseClassDeclaration(Instruction instruction, BeeSource beeSource) { + if (instruction instanceof ClassInstruction) { + ClassInstruction classDeclaration = (ClassInstruction) instruction; ClassOperation operation = classDeclaration.getOperation(); switch (operation) { case CLASS -> { + beeSource.addAccessFlags(ClassAccessFlags.SUPER); + beeSource.setClassFileVersion(getVersion(classDeclaration)); + String[] tokens = split(classDeclaration.getOperand(), parensplitter); - beeSource.addAccessFlags(ClassAccessFlags.PUBLIC, ClassAccessFlags.SUPER); - beeSource.setName(tokens[0]); - beeSource.setClassFileVersion(Version.get(tokens[1]).orElseThrow(illegalArgument(tokens[1]))); + String rightHand = classDeclaration.getOperand(); + if (tokens.length > 0) { // has Version tag + rightHand = tokens[0]; + } + tokens = rightHand.split(" "); + + if (tokens.length == 2) { // has access flag + beeSource.addAccessFlags(ClassAccessFlags.valueOf(tokens[0].toUpperCase())); + } + beeSource.setName(getClassName(tokens)); } case INTERFACE -> { }//TODO @@ -194,6 +204,23 @@ public class SourceCompiler { currentLine += 1; } + private Version getVersion(ClassInstruction classDeclaration) { + String[] tokens2 = split(classDeclaration.getOperand(), parensplitter); + if (tokens2.length == 0) { + return Version.V8; + } else { + return Version.get(tokens2[1]).orElseThrow(illegalArgument(tokens2[1])); + } + } + + private String getClassName(String[] tokens) { + if (tokens.length == 2) { + return tokens[1]; + } else { + return tokens[0]; + } + } + private Instruction compileLine(String line) { if (!line.startsWith(" ")) { String[] tokens = split(line, firstBlanksplitter); @@ -203,6 +230,9 @@ public class SourceCompiler { return new ClassInstruction(classOperation, operand); } else { line = line.substring(2); + if (line.startsWith(" ")) { + throw new IllegalArgumentException("Illegal indent -> must be 2 spaces"); + } String operation; String operand; if (line.indexOf(' ') > -1) { @@ -236,7 +266,13 @@ public class SourceCompiler { try { return switch (type) { case "int" -> int.class; + case "short" -> short.class; + case "byte" -> byte.class; + case "long" -> long.class; + case "float" -> float.class; case "double" -> double.class; + case "boolean" -> boolean.class; + case "char" -> char.class; default -> Class.forName(type); }; } catch (ClassNotFoundException e) { diff --git a/src/main/java/nl/sander/beejava/TypeMapper.java b/src/main/java/nl/sander/beejava/TypeMapper.java index cbd5aee..aed69a8 100644 --- a/src/main/java/nl/sander/beejava/TypeMapper.java +++ b/src/main/java/nl/sander/beejava/TypeMapper.java @@ -16,8 +16,7 @@ public class TypeMapper { MAP.put(long.class, "J"); MAP.put(short.class, "S"); MAP.put(boolean.class, "Z"); - MAP.put(Void.class, "V"); - + MAP.put(void.class, "V"); } //TODO something with arrays diff --git a/src/main/java/nl/sander/beejava/api/BeeConstructor.java b/src/main/java/nl/sander/beejava/api/BeeConstructor.java index e195c1a..feda15f 100644 --- a/src/main/java/nl/sander/beejava/api/BeeConstructor.java +++ b/src/main/java/nl/sander/beejava/api/BeeConstructor.java @@ -9,19 +9,15 @@ import java.util.*; */ public final class BeeConstructor extends CodeContainer { - public BeeConstructor(Set accessFlags, - Set formalParameters, - List code) { + public BeeConstructor(BeeSource beeSource, Set accessFlags, + Set formalParameters, + List code) { this.formalParameters.addAll(formalParameters); this.accessFlags.addAll(accessFlags); super.code.addAll(code); + setOwner(beeSource); } - public static Builder builder() { - return new Builder(); - } - - public String getName() { return ""; } @@ -34,7 +30,7 @@ public final class BeeConstructor extends CodeContainer { } public Class getReturnType() { - return Void.class; + return void.class; } @Override @@ -55,34 +51,4 @@ public final class BeeConstructor extends CodeContainer { return Objects.hash(formalParameters); } - public static class Builder { - private final Set accessFlags = new HashSet<>(); - private final Set formalParameters = new HashSet<>(); - private final List code = new LinkedList<>(); - - private Builder() { - - } - - public Builder withFormalParameters(BeeParameter... formalParameters) { - this.formalParameters.addAll(Arrays.asList(formalParameters)); - return this; - } - - public Builder withAccessFlags(MethodAccessFlag... accessFlags) { - this.accessFlags.addAll(Arrays.asList(accessFlags)); - return this; - } - - public Builder withCode(CodeLine... lines) { - this.code.addAll(Arrays.asList(lines)); - return this; - } - - public BeeConstructor build() { - BeeConstructor beeConstructor = new BeeConstructor(accessFlags, formalParameters, code); - code.forEach(line -> line.setOwner(beeConstructor)); - return beeConstructor; - } - } } diff --git a/src/main/java/nl/sander/beejava/api/BeeField.java b/src/main/java/nl/sander/beejava/api/BeeField.java index 3683b9d..09eea60 100644 --- a/src/main/java/nl/sander/beejava/api/BeeField.java +++ b/src/main/java/nl/sander/beejava/api/BeeField.java @@ -2,7 +2,6 @@ package nl.sander.beejava.api; import nl.sander.beejava.flags.FieldAccessFlag; -import java.util.Arrays; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -12,20 +11,25 @@ import java.util.Set; */ public final class BeeField { + private final String declaringClass; private final Set accessFlags = new HashSet<>(); private final Class type; private final String name; - public BeeField(Set accessFlags, Class type, String name) { + /** + * + * @param declaringClass class that declares the field. Can be existing class, or the one that is under construction + * @param accessFlags + * @param type field type. Must be existing type. + * @param name + */ + public BeeField(String declaringClass, Set accessFlags, Class type, String name) { + this.declaringClass = declaringClass; this.accessFlags.addAll(accessFlags); this.type = type; this.name = name; } - public static BeeField.Builder builder(){ - return new Builder(); - } - public Set getAccessFlags() { return accessFlags; } @@ -38,6 +42,10 @@ public final class BeeField { return name; } + public String getDeclaringClass() { + return declaringClass; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -51,32 +59,5 @@ public final class BeeField { return Objects.hash(name); } - public static class Builder { - private final Set accessFlags = new HashSet<>(); - private Class type; - private String name; - private Builder(){ - - } - - public BeeField.Builder withAccessFlags(FieldAccessFlag... accessFlags) { - this.accessFlags.addAll(Arrays.asList(accessFlags)); - return this; - } - - public BeeField.Builder withType(Class type) { - this.type=type; - return this; - } - - public BeeField.Builder withName(String name) { - this.name=name; - return this; - } - - public BeeField build() { - return new BeeField(accessFlags, type, name); - } - } } diff --git a/src/main/java/nl/sander/beejava/api/BeeMethod.java b/src/main/java/nl/sander/beejava/api/BeeMethod.java index 0454bdf..fa5c406 100644 --- a/src/main/java/nl/sander/beejava/api/BeeMethod.java +++ b/src/main/java/nl/sander/beejava/api/BeeMethod.java @@ -12,18 +12,15 @@ public final class BeeMethod extends CodeContainer { private final Class returnType; - private BeeMethod(String name, Set accessFlags, - List formalParameters, - Class returnType, List code) { + public BeeMethod(BeeSource beeSource, String name, Set accessFlags, + Set formalParameters, + Class returnType, List code) { this.name = name; this.accessFlags.addAll(accessFlags); this.formalParameters.addAll(formalParameters); this.returnType = returnType; super.code.addAll(code); - } - - public static Builder builder() { - return new Builder(); + setOwner(beeSource); } public String getName() { @@ -48,45 +45,18 @@ public final class BeeMethod extends CodeContainer { */ } - public static class Builder { - private final Set accessFlags = new HashSet<>(); - private final List formalParameters = new LinkedList<>(); - private final List code = new LinkedList<>(); - private String name; - private Class returnType = Void.class; + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + BeeMethod beeMethod = (BeeMethod) o; + return name.equals(beeMethod.name) && + returnType.equals(beeMethod.returnType); + } - private Builder() { - } - - public Builder withName(String name) { - this.name = name; - return this; - } - - public Builder withAccessFlags(MethodAccessFlag... accessFlags) { - this.accessFlags.addAll(Arrays.asList(accessFlags)); - return this; - } - - public Builder withFormalParameters(BeeParameter... formalParameters) { - this.formalParameters.addAll(Arrays.asList(formalParameters)); - return this; - } - - public Builder withReturnType(Class returnType) { - this.returnType = returnType; - return this; - } - - public Builder withCode(CodeLine... lines) { - this.code.addAll(Arrays.asList(lines)); - return this; - } - - public BeeMethod build() { - BeeMethod beeMethod = new BeeMethod(name, accessFlags, formalParameters, returnType, code); - code.forEach(line -> line.setOwner(beeMethod)); - return beeMethod; - } + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), name, returnType); } } diff --git a/src/main/java/nl/sander/beejava/api/BeePackage.java b/src/main/java/nl/sander/beejava/api/BeePackage.java deleted file mode 100644 index 9638a22..0000000 --- a/src/main/java/nl/sander/beejava/api/BeePackage.java +++ /dev/null @@ -1,17 +0,0 @@ -package nl.sander.beejava.api; - -/** - * Contains the name of the package for a class - */ -public final class BeePackage { - - private final String name; - - BeePackage(String name) { - this.name = name; - } - - public String getName() { - return name; - } -} diff --git a/src/main/java/nl/sander/beejava/api/BeeParameter.java b/src/main/java/nl/sander/beejava/api/BeeParameter.java index c4dd604..1533c8e 100644 --- a/src/main/java/nl/sander/beejava/api/BeeParameter.java +++ b/src/main/java/nl/sander/beejava/api/BeeParameter.java @@ -8,14 +8,18 @@ import java.util.Objects; public final class BeeParameter { private final Class type; private final String name; + private final int index; public BeeParameter(Class type, String name) { this.type = type; this.name = name; + this.index = -1; } - public static BeeParameter create(Class type, String name) { - return new BeeParameter(type, Objects.requireNonNull(name)); + public BeeParameter(Class type, String name, int index) { + this.type = type; + this.name = name; + this.index = index; } public Class getType() { @@ -34,6 +38,10 @@ public final class BeeParameter { return name.equals(that.name); } + public int getIndex() { + return index; + } + @Override public int hashCode() { return Objects.hash(name); diff --git a/src/main/java/nl/sander/beejava/api/BeeSource.java b/src/main/java/nl/sander/beejava/api/BeeSource.java index 410de1e..83df8cc 100644 --- a/src/main/java/nl/sander/beejava/api/BeeSource.java +++ b/src/main/java/nl/sander/beejava/api/BeeSource.java @@ -2,50 +2,22 @@ package nl.sander.beejava.api; import nl.sander.beejava.flags.ClassAccessFlags; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** - * Contains all information needed for compilation - * - * End users need to create an instance of this class (using the Builder) and add all fields, methods etc. - * Once created the BeeSource object is immutable. + * Contains parsed class elements like constructors and methods, but the opcode is not compiled to bytecode yet. */ public final class BeeSource { - private final Version classFileVersion; - private final BeePackage beePackage; private final Set accessFlags = new HashSet<>(); - private final String simpleName; - private final Class superClass; private final Set> interfaces = new HashSet<>(); private final Set fields = new HashSet<>(); private final Set constructors = new HashSet<>(); private final Set methods = new HashSet<>(); - - private BeeSource(Version classFileVersion, - BeePackage beePackage, Set accessFlags, String simpleName, Class superClass, - Set> interfaces, Set fields, Set constructors, Set methods) { - this.classFileVersion = classFileVersion; - this.beePackage = beePackage; - this.accessFlags.addAll(accessFlags); - this.simpleName = simpleName; - this.superClass = superClass; - this.interfaces.addAll(interfaces); - this.fields.addAll(fields); - this.constructors.addAll(constructors); - this.methods.addAll(methods); - } - - /** - * Create a new BeeSource Builder class. - * - * @return a new instance of a Builder - */ - public static BeeSource.Builder builder() { - return new Builder(); - } + private Version classFileVersion; + private String name; + private Class superClass = Object.class; /** * @return The classfile version @@ -54,18 +26,8 @@ public final class BeeSource { return classFileVersion; } - /** - * @return The package in which the compiled class will reside. - */ - public BeePackage getPackage() { - return beePackage; - } - - /** - * returns the unqualified name, like java.lang.Class - */ - public String getSimpleName() { - return simpleName; + public void setClassFileVersion(Version classFileVersion) { + this.classFileVersion = classFileVersion; } /** @@ -75,6 +37,10 @@ public final class BeeSource { return Collections.unmodifiableSet(constructors); } + public void addConstructors(BeeConstructor... constructors) { + this.constructors.addAll(Set.of(constructors)); + } + /** * @return all methods that are provided with the class */ @@ -82,6 +48,10 @@ public final class BeeSource { return methods; } + public void addMethods(BeeMethod... methods) { + this.methods.addAll(Set.of(methods)); + } + /** * @return The access flags for the class */ @@ -89,11 +59,19 @@ public final class BeeSource { return Collections.unmodifiableSet(accessFlags); } + public void addAccessFlags(ClassAccessFlags... classAccessFlags) { + this.accessFlags.addAll(Set.of(classAccessFlags)); + } + /** * @return The full name, like java.lang.Class */ public String getName() { - return beePackage.getName() + "." + simpleName; + return name; + } + + public void setName(String name) { + this.name = name; } /** @@ -103,6 +81,10 @@ public final class BeeSource { return superClass; } + public void setSuperClass(Class superClass) { + this.superClass = superClass; + } + /** * @return a list of unique interfaces that the class will implements */ @@ -110,6 +92,10 @@ public final class BeeSource { return Collections.unmodifiableSet(interfaces); } + public void addInterfaces(Class... interfaces) { + this.interfaces.addAll(Set.of(interfaces)); + } + /** * @return a list of unique fields that the class will contain */ @@ -117,74 +103,14 @@ public final class BeeSource { return Collections.unmodifiableSet(fields); } - /** - * Helper class for creating BeeSource classes - */ - public static class Builder { - private final Set accessFlags = new HashSet<>(); - private final Set> interfaces = new HashSet<>(); - private final Set fields = new HashSet<>(); - private Version version; - private BeePackage beePackage; - private Class superClass = Object.class; - private String simpleName; - private final Set constructors = new HashSet<>(); - private final Set methods = new HashSet<>(); - - private Builder() { - } - - public Builder withClassFileVersion(Version version) { - this.version = version; - return this; - } - - public BeeSource.Builder withPackage(String beePackage) { - this.beePackage = new BeePackage(beePackage); - return this; - } - - public BeeSource.Builder withAccessFlags(ClassAccessFlags... accessFlags) { - this.accessFlags.addAll(Arrays.asList(accessFlags)); - return this; - } - - public BeeSource.Builder withSimpleName(String simpleName) { - this.simpleName = simpleName; - return this; - } - - public Builder withSuperClass(Class superClass) { - this.superClass = superClass; - return this; - } - - public Builder withInterfaces(Class... interfaces) { - this.interfaces.addAll(Arrays.asList(interfaces)); - return this; - } - - public Builder withFields(BeeField... fields) { - this.fields.addAll(Arrays.asList(fields)); - return this; - } - - public Builder withConstructors(BeeConstructor... constructors) { - this.constructors.addAll(Arrays.asList(constructors)); - return this; - } - - public Builder withMethods(BeeMethod... methods) { - this.methods.addAll(Arrays.asList(methods)); - return this; - } - - public BeeSource build() { - BeeSource beeSource = new BeeSource(version, beePackage, accessFlags, simpleName, superClass, interfaces, fields, constructors, methods); - constructors.forEach(c -> c.setOwner(beeSource)); - methods.forEach(m -> m.setOwner(beeSource)); - return beeSource; - } + public void addFields(BeeField... fields) { + this.fields.addAll(Set.of(fields)); + } + public BeeField getField(String fieldName) { + return fields.stream() + .filter(f -> f.getName().equals(fieldName)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("field " + fieldName + " not found in " + getName())); } } diff --git a/src/main/java/nl/sander/beejava/apiv2/ClassInstruction.java b/src/main/java/nl/sander/beejava/api/ClassInstruction.java similarity index 92% rename from src/main/java/nl/sander/beejava/apiv2/ClassInstruction.java rename to src/main/java/nl/sander/beejava/api/ClassInstruction.java index f16e12a..ce1f574 100644 --- a/src/main/java/nl/sander/beejava/apiv2/ClassInstruction.java +++ b/src/main/java/nl/sander/beejava/api/ClassInstruction.java @@ -1,4 +1,4 @@ -package nl.sander.beejava.apiv2; +package nl.sander.beejava.api; public class ClassInstruction extends Instruction { private final ClassOperation classOperation; diff --git a/src/main/java/nl/sander/beejava/apiv2/ClassOperation.java b/src/main/java/nl/sander/beejava/api/ClassOperation.java similarity index 80% rename from src/main/java/nl/sander/beejava/apiv2/ClassOperation.java rename to src/main/java/nl/sander/beejava/api/ClassOperation.java index 793562c..35f5168 100644 --- a/src/main/java/nl/sander/beejava/apiv2/ClassOperation.java +++ b/src/main/java/nl/sander/beejava/api/ClassOperation.java @@ -1,4 +1,4 @@ -package nl.sander.beejava.apiv2; +package nl.sander.beejava.api; import java.util.Optional; @@ -10,7 +10,7 @@ public enum ClassOperation { CONSTRUCTOR, METHOD; - static Optional get(String text){ + public static Optional get(String text){ String upper = text.toUpperCase(); for (ClassOperation val: ClassOperation.values()){ if (val.toString().equals(upper)){ diff --git a/src/main/java/nl/sander/beejava/api/CodeContainer.java b/src/main/java/nl/sander/beejava/api/CodeContainer.java index deea548..8110d5b 100644 --- a/src/main/java/nl/sander/beejava/api/CodeContainer.java +++ b/src/main/java/nl/sander/beejava/api/CodeContainer.java @@ -1,9 +1,7 @@ package nl.sander.beejava.api; +import nl.sander.beejava.JavaInstruction; import nl.sander.beejava.TypeMapper; -import nl.sander.beejava.api.BeeParameter; -import nl.sander.beejava.api.BeeSource; -import nl.sander.beejava.api.CodeLine; import nl.sander.beejava.flags.MethodAccessFlag; import java.util.*; @@ -17,6 +15,7 @@ public abstract class CodeContainer { protected final List code = new LinkedList<>(); protected final Set formalParameters = new HashSet<>(); protected final Set accessFlags = new HashSet<>(); + private final List expandedCode = new ArrayList<>(); private BeeSource owner; public List getCode() { @@ -53,9 +52,35 @@ public abstract class CodeContainer { this.owner = beeSource; } + public List getExpandedCode() { + return Collections.unmodifiableList(expandedCode); + } + + public void setExpandedCode(List instructions) { + expandedCode.clear(); + expandedCode.addAll(instructions); + } + public abstract boolean isConstructor(); public Set getFormalParameters() { return formalParameters; } + + public Optional getParameter(String parameterName) { + return formalParameters.stream().filter(p -> parameterName.equals(p.getName())).findAny(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CodeContainer that = (CodeContainer) o; + return formalParameters.equals(that.formalParameters); + } + + @Override + public int hashCode() { + return Objects.hash(formalParameters); + } } diff --git a/src/main/java/nl/sander/beejava/api/CodeLine.java b/src/main/java/nl/sander/beejava/api/CodeLine.java index d4e59c2..4d53588 100644 --- a/src/main/java/nl/sander/beejava/api/CodeLine.java +++ b/src/main/java/nl/sander/beejava/api/CodeLine.java @@ -1,249 +1,28 @@ package nl.sander.beejava.api; -import nl.sander.beejava.JavaOpcode; -import nl.sander.beejava.TypeMapper; -import nl.sander.beejava.constantpool.entry.ConstantPoolEntry; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -public final class CodeLine { +public class CodeLine extends Instruction { private final Opcode opcode; - private Ref ref; - private BeeParameter parameter; - private Class type; - private String methodName; - private List> inputSignature; - private String outputSignature; - private BeeField ownfield; // when you create a class with a field, you can refer to it - private Field externalfield; // when you refer to a field from another class - private Object constValue; - private ConstantPoolEntry assignedEntry; private CodeContainer owner; - private JavaOpcode javaOpcode; - CodeLine(Opcode opcode) { + public CodeLine(Opcode opcode, String operand) { + super(operand); this.opcode = opcode; } - public static CodeLine line(Opcode opcode, Ref ref) { - return new CodeLine(opcode).withRef(ref); - } - - public static CodeLine line(Opcode opcode, String fieldClass, String fieldName) { - return new CodeLine(opcode).withExternalFieldRef(fieldClass, fieldName); - } - - public static CodeLine line(Opcode opcode, String constValue) { - return new CodeLine(opcode).withConstValue(constValue); - } - - public static CodeLine line(Opcode opcode, String className, String methodName, String inputSignature) throws ClassNotFoundException { - return new CodeLine(opcode).withClassName(className).withMethodName(methodName).withInput(parse(inputSignature)).withVoidOutput(); - } - - public static CodeLine line(Opcode opcode, - Ref ref, String methodNameRef, String inputSignature) throws ClassNotFoundException { - return new CodeLine(opcode).withRef(ref).withMethodName(methodNameRef).withInput(parse(inputSignature)).withVoidOutput(); - } - - public static CodeLine line(Opcode opcode, - Ref ref, String methodNameRef, String inputSignature, String outputSignature) throws ClassNotFoundException { - return new CodeLine(opcode).withRef(ref).withMethodName(methodNameRef).withInput(parse(inputSignature)).withOutput(outputSignature); - } - - public static CodeLine line(Opcode opcode) { - return new CodeLine(opcode); - } - - public static CodeLine line(Opcode opcode, BeeParameter parameter) { - return new CodeLine(opcode).withParameter(parameter); - } - - public static CodeLine line(Opcode opcode, BeeField intField) { - return new CodeLine(opcode).withRef(Ref.THIS).withOwnField(intField); - } - - private CodeLine withRef(Ref ref) { - this.ref = ref; - return this; - } - - private CodeLine withClassName(String className) { - this.type = loadClass(className); - return this; - } - - private CodeLine withMethodName(String methodName) { - this.methodName = methodName; - return this; - } - - private CodeLine withInput(List> inputSignature) { - this.inputSignature = inputSignature; - return this; - } - - private CodeLine withVoidOutput() { - return withOutput("V"); - } - - private CodeLine withOutput(String outputSignature) { - this.outputSignature = outputSignature; - return this; - } - - private CodeLine withParameter(BeeParameter parameter) { - this.parameter = parameter; - return this; - } - - private CodeLine withConstValue(Object constValue) { - this.constValue = constValue; - return this; //TODO - } - - private CodeLine withOwnField(BeeField beeField) { - this.ownfield = beeField; - return this; - } - - private CodeLine withExternalFieldRef(String className, String field) { - this.type = loadClass(className); - this.externalfield = loadField(field); - return this; - } - -// TODO decide whether to work with Strings or class objects... - - /* - * Look up the type of a field in a class - * - * Assumes field is accessible - * - * @param className the class containing a field - * @param field name of the field - * @return the field type - */ - private Field loadField(String field) { - try { - return type.getField(field); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } - } - - public boolean hasClassName() { - return type != null; - } - - - public String getClassName() { - return type.getName(); - } - - public String getMethodName() { - return methodName; - } - - public boolean hasMethodCall() { - return methodName != null; - } - - public String getMethodSignature() { - return inputSignature.stream() - .map(TypeMapper::map) - .collect(Collectors.joining(",", "(", ")")) + outputSignature; - } - - public Ref getRef() { - return ref; - } - - public boolean hasRef() { - return ref != null; - } - - public boolean hasRefToOwnField() { - return ownfield != null; - } - - - public Object getConstValue() { - return constValue; - } - - public boolean hasConstValue() { - return constValue != null; - } - - public BeeField getOwnfield() { - return ownfield; - } - - public BeeParameter getParameter() { - return parameter; - } - - - public boolean hasRefToExternalField() { - return externalfield != null; - } - - public Field getExternalfield() { - return externalfield; - } - - public ConstantPoolEntry getAssignedEntry() { - return assignedEntry; - } - - public void setAssignedEntry(ConstantPoolEntry assignedEntry) { - this.assignedEntry = assignedEntry; + public String getOperand() { + return operand; } public Opcode getOpcode() { return opcode; } - private Class loadClass(String className) { - try { - return Class.forName(className); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); //TODO specific exception - } + public void setOwner(CodeContainer owner) { + this.owner = owner; } public CodeContainer getOwner() { return owner; } - - public void setOwner(CodeContainer codeContainer) { - this.owner = codeContainer; - } - - public JavaOpcode getJavaOpcode() { - return javaOpcode; - } - - public void setJavaOpcode(JavaOpcode javaOpcode) { - this.javaOpcode = javaOpcode; - } - - private static List> parse(String inputSignature) throws ClassNotFoundException { - if ("()".equals(inputSignature)) { - return Collections.emptyList(); - } else { - String[] params = inputSignature.split(","); - List> paramClasses = new ArrayList<>(); - for (String param : params) { - paramClasses.add(Class.forName(param)); - } - return paramClasses; - } - } } diff --git a/src/main/java/nl/sander/beejava/apiv2/Instruction.java b/src/main/java/nl/sander/beejava/api/Instruction.java similarity index 81% rename from src/main/java/nl/sander/beejava/apiv2/Instruction.java rename to src/main/java/nl/sander/beejava/api/Instruction.java index cbcd007..ec3eb88 100644 --- a/src/main/java/nl/sander/beejava/apiv2/Instruction.java +++ b/src/main/java/nl/sander/beejava/api/Instruction.java @@ -1,4 +1,4 @@ -package nl.sander.beejava.apiv2; +package nl.sander.beejava.api; public class Instruction { protected final String operand; diff --git a/src/main/java/nl/sander/beejava/api/Opcode.java b/src/main/java/nl/sander/beejava/api/Opcode.java index 5b3dc31..df3af2d 100644 --- a/src/main/java/nl/sander/beejava/api/Opcode.java +++ b/src/main/java/nl/sander/beejava/api/Opcode.java @@ -1,71 +1,65 @@ package nl.sander.beejava.api; +import java.util.Optional; + public enum Opcode { - LD_VAR("ld_var"), - LD_CONST("ld_const"), - LD_FIELD("ld_field"), - STORE("store"), - CONST("const"), - NEWARRAY("new"), - RETURN("return"), - ARRAYLENGTH("length"), - THROW("throw"), - PUSH("push"), - CHECKCAST("checkcast"), - ADD("add"), - COMPARE("cmp"), - DIVIDE("div"), - MULTIPLY("mul"), - NEGATE("neg"), - REMAINDER("rem"), - SUBTRACT("sub"), - DUPLICATE("dup"), - DUPLICATE_DOWN("dup_x1"), - DUPLICATE2("dup2"), - DUPLICATE2_DOWN("dup2_x1"), - GET("get"), - GOTO("goto"), - GOTO_W("goto_w"), - TO_DOUBLE("2d"), - TO_INT("2i"), - TO_LONG("2l"), - TO_BYTE("2b"), - TO_CHAR("2c"), - TO_FLOAT("2f"), - TO_SHORT("2s"), - AND("and"), - IF_EQUAL("ifeq"), - IF_NOT_EQUAL("ifneq"), - IF_LESS_THAN("iflt"), - IF_GREATER_OR_EQUAL("ifge"), - IF_GREATER_THAN("ifgt"), - IF_LESS_OR_EQUAL("ifle"), - IF_NOT_NULL("ifnotnull"), - IF_NULL("ifnull"), - INCREMENT("inc"), - INSTANCEOF("instanceof"), - INVOKE("invoke"), - OR("or"), - SHIFT_LEFT("shr"), - SHIFT_RIGHT("shl"), - LOGICAL_SHIFT_RIGHT("ushr"), - XOR("xor"), - LOOKUPSWITCH("lookupswitch"), - MONITORENTER("monitorenter"), - MONITOREXIT("monitorexit"), - MULTIANEWARRAY("multinewarray"), - NEW("new"), - NOP("nop"), - POP("pop"), - POP2("pop2"), - PUT("put"), - SWAP("swap"), - TABLESWITCH("tableswitch"), - WIDE("wide"); + LOAD, + STORE, + CONST, + RETURN, + ARRAYLENGTH, + THROW, + CAST, + ADD, + COMPARE, + DIVIDE, + MULTIPLY, + NEGATE, + REMAINDER, + SUBTRACT, + GET, + GOTO, + TO_DOUBLE, + TO_INT, + TO_LONG, + TO_BYTE, + TO_CHAR, + TO_FLOAT, + TO_SHORT, + AND, + IF_EQUAL, + IF_NOT_EQUAL, + IF_LESS_THAN, + IF_GREATER_OR_EQUAL, + IF_GREATER_THAN, + IF_LESS_OR_EQUAL, + IF_NOT_NULL, + IF_NULL, + INCREMENT, + INSTANCEOF, + INVOKE, + OR, + SHIFT_LEFT, + SHIFT_RIGHT, + LOGICAL_SHIFT_RIGHT, + XOR, + LOOKUPSWITCH, + MONITORENTER, + MONITOREXIT, + NEW, + NEWARRAY, + MULTIANEWARRAY, + PUT, + SWAP, + TABLESWITCH, + WIDE; - private final String name; - - Opcode(String name) { - this.name=name; + public static Optional get(String text) { + for (Opcode opcode : Opcode.values()) { + if (opcode.toString().equals(text)) { + return Optional.of(opcode); + } + } + return Optional.empty(); } } diff --git a/src/main/java/nl/sander/beejava/api/Ref.java b/src/main/java/nl/sander/beejava/api/Ref.java deleted file mode 100644 index 49ea11a..0000000 --- a/src/main/java/nl/sander/beejava/api/Ref.java +++ /dev/null @@ -1,9 +0,0 @@ -package nl.sander.beejava.api; - -/** - * used to indicate this, super, or none of those, in which case it's class - */ -public enum Ref { - THIS, - SUPER -} diff --git a/src/main/java/nl/sander/beejava/api/Version.java b/src/main/java/nl/sander/beejava/api/Version.java index 4ddea20..292f416 100644 --- a/src/main/java/nl/sander/beejava/api/Version.java +++ b/src/main/java/nl/sander/beejava/api/Version.java @@ -1,6 +1,6 @@ package nl.sander.beejava.api; -import nl.sander.beejava.apiv2.Opcode; +import nl.sander.beejava.api.Opcode; import java.util.Optional; diff --git a/src/main/java/nl/sander/beejava/apiv2/BeeConstructor.java b/src/main/java/nl/sander/beejava/apiv2/BeeConstructor.java deleted file mode 100644 index a4ea53a..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/BeeConstructor.java +++ /dev/null @@ -1,53 +0,0 @@ -package nl.sander.beejava.apiv2; - -import nl.sander.beejava.flags.MethodAccessFlag; - -import java.util.*; - -/** - * Models a constructor - */ -public final class BeeConstructor extends CodeContainer { - - public BeeConstructor(Set accessFlags, - Set formalParameters, - List code) { - this.formalParameters.addAll(formalParameters); - this.accessFlags.addAll(accessFlags); - super.code.addAll(code); - } - - public String getName() { - return ""; - } - - @Override - public String toString() { - return "BeeConstructor{" + - "formalParameters=" + formalParameters + - '}'; - } - - public Class getReturnType() { - return Void.class; - } - - @Override - public boolean isConstructor() { - return true; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BeeConstructor that = (BeeConstructor) o; - return formalParameters.equals(that.formalParameters); - } - - @Override - public int hashCode() { - return Objects.hash(formalParameters); - } - -} diff --git a/src/main/java/nl/sander/beejava/apiv2/BeeField.java b/src/main/java/nl/sander/beejava/apiv2/BeeField.java deleted file mode 100644 index 81e012e..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/BeeField.java +++ /dev/null @@ -1,82 +0,0 @@ -package nl.sander.beejava.apiv2; - -import nl.sander.beejava.flags.FieldAccessFlag; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -/** - * Models a field in a BeeClass - */ -public final class BeeField { - - private final Set accessFlags = new HashSet<>(); - private final Class type; - private final String name; - - public BeeField(Set accessFlags, Class type, String name) { - this.accessFlags.addAll(accessFlags); - this.type = type; - this.name = name; - } - - public static Builder builder(){ - return new Builder(); - } - - public Set getAccessFlags() { - return accessFlags; - } - - public Class getType() { - return type; - } - - public String getName() { - return name; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BeeField beeField = (BeeField) o; - return name.equals(beeField.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); - } - - public static class Builder { - private final Set accessFlags = new HashSet<>(); - private Class type; - private String name; - - private Builder(){ - - } - - public Builder withAccessFlags(FieldAccessFlag... accessFlags) { - this.accessFlags.addAll(Arrays.asList(accessFlags)); - return this; - } - - public Builder withType(Class type) { - this.type=type; - return this; - } - - public Builder withName(String name) { - this.name=name; - return this; - } - - public BeeField build() { - return new BeeField(accessFlags, type, name); - } - } -} diff --git a/src/main/java/nl/sander/beejava/apiv2/BeeMethod.java b/src/main/java/nl/sander/beejava/apiv2/BeeMethod.java deleted file mode 100644 index d29d62c..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/BeeMethod.java +++ /dev/null @@ -1,61 +0,0 @@ -package nl.sander.beejava.apiv2; - -import nl.sander.beejava.flags.MethodAccessFlag; - -import java.util.*; - -/** - * Models a method in a BeeClass - */ -public final class BeeMethod extends CodeContainer { - private final String name; - - private final Class returnType; - - public BeeMethod(String name, Set accessFlags, - Set formalParameters, - Class returnType, List code) { - this.name = name; - this.accessFlags.addAll(accessFlags); - this.formalParameters.addAll(formalParameters); - this.returnType = returnType; - super.code.addAll(code); - } - - public String getName() { - return name; - } - - public Class getReturnType() { - return returnType; - } - - @Override - public boolean isConstructor() { - return false; - } - - public void validate() { - //TODO - /* - * here we could add checks like: - * -If this method is in a class rather than an interface, and the name of the method is , then the descriptor must denote a void method. - * -If the name of the method is , then the descriptor must denote avoid method, and, in a class file whose version number is 51.0 or above,a method that takes no arguments - */ - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - if (!super.equals(o)) return false; - BeeMethod beeMethod = (BeeMethod) o; - return name.equals(beeMethod.name) && - returnType.equals(beeMethod.returnType); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), name, returnType); - } -} diff --git a/src/main/java/nl/sander/beejava/apiv2/BeeParameter.java b/src/main/java/nl/sander/beejava/apiv2/BeeParameter.java deleted file mode 100644 index 769126d..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/BeeParameter.java +++ /dev/null @@ -1,37 +0,0 @@ -package nl.sander.beejava.apiv2; - -import java.util.Objects; - -/** - * Models a formal parameter in a method declaration. - */ -public final class BeeParameter { - private final Class type; - private final String name; - - public BeeParameter(Class type, String name) { - this.type = type; - this.name = name; - } - - public Class getType() { - return type; - } - - public String getName() { - return name; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - BeeParameter that = (BeeParameter) o; - return name.equals(that.name); - } - - @Override - public int hashCode() { - return Objects.hash(name); - } -} diff --git a/src/main/java/nl/sander/beejava/apiv2/BeeSource.java b/src/main/java/nl/sander/beejava/apiv2/BeeSource.java deleted file mode 100644 index 4bfabd1..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/BeeSource.java +++ /dev/null @@ -1,110 +0,0 @@ -package nl.sander.beejava.apiv2; - -import nl.sander.beejava.api.Version; -import nl.sander.beejava.flags.ClassAccessFlags; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * Contains parsed class elements like constructors and methods, but the opcode is not compiled to bytecode yet. - */ -public final class BeeSource { - private final Set accessFlags = new HashSet<>(); - private final Set> interfaces = new HashSet<>(); - private final Set fields = new HashSet<>(); - private final Set constructors = new HashSet<>(); - private final Set methods = new HashSet<>(); - private Version classFileVersion; - private String name; - private Class superClass; - - /** - * @return The classfile version - */ - public Version getClassFileVersion() { - return classFileVersion; - } - - public void setClassFileVersion(Version classFileVersion) { - this.classFileVersion = classFileVersion; - } - - /** - * @return all constructors that are provided with the class - */ - public Set getConstructors() { - return Collections.unmodifiableSet(constructors); - } - - public void addConstructors(BeeConstructor... constructors) { - this.constructors.addAll(Set.of(constructors)); - } - - /** - * @return all methods that are provided with the class - */ - public Set getMethods() { - return methods; - } - - public void addMethods(BeeMethod... methods) { - this.methods.addAll(Set.of(methods)); - } - - /** - * @return The access flags for the class - */ - public Set getAccessFlags() { - return Collections.unmodifiableSet(accessFlags); - } - - public void addAccessFlags(ClassAccessFlags... classAccessFlags) { - this.accessFlags.addAll(Set.of(classAccessFlags)); - } - - /** - * @return The full name, like java.lang.Class - */ - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * @return The superclass - */ - public Class getSuperClass() { - return superClass; - } - - public void setSuperClass(Class superClass) { - this.superClass = superClass; - } - - /** - * @return a list of unique interfaces that the class will implements - */ - public Set> getInterfaces() { - return Collections.unmodifiableSet(interfaces); - } - - public void addInterfaces(Class... interfaces) { - this.interfaces.addAll(Set.of(interfaces)); - } - - /** - * @return a list of unique fields that the class will contain - */ - public Set getFields() { - return Collections.unmodifiableSet(fields); - } - - public void addFields(BeeField... fields) { - this.fields.addAll(Set.of(fields)); - } -} diff --git a/src/main/java/nl/sander/beejava/apiv2/CodeContainer.java b/src/main/java/nl/sander/beejava/apiv2/CodeContainer.java deleted file mode 100644 index 2a3829b..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/CodeContainer.java +++ /dev/null @@ -1,67 +0,0 @@ -package nl.sander.beejava.apiv2; - -import nl.sander.beejava.TypeMapper; -import nl.sander.beejava.flags.MethodAccessFlag; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * parent of a constructor or a method. - */ -public abstract class CodeContainer { - - protected final List code = new LinkedList<>(); - protected final Set formalParameters = new HashSet<>(); - protected final Set accessFlags = new HashSet<>(); - private BeeSource owner; - - public List getCode() { - return code; - } - - public String getSignature() { - return getParametersSignature() + TypeMapper.map(getReturnType()); - } - - public abstract String getName(); - - public abstract Class getReturnType(); - - private String getParametersSignature() { - return formalParameters.stream() - .map(BeeParameter::getType) - .map(TypeMapper::map) - .collect(Collectors.joining(",", "(", ")")); - } - - public Set getAccessFlags() { - return accessFlags; - } - - public BeeSource getOwner() { - return owner; - } - - public void setOwner(BeeSource beeSource) { - if (owner != null) { - throw new IllegalStateException("Owner set twice. Sue the developer!"); - } - this.owner = beeSource; - } - - public abstract boolean isConstructor(); - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CodeContainer that = (CodeContainer) o; - return formalParameters.equals(that.formalParameters); - } - - @Override - public int hashCode() { - return Objects.hash(formalParameters); - } -} diff --git a/src/main/java/nl/sander/beejava/apiv2/CodeLine.java b/src/main/java/nl/sander/beejava/apiv2/CodeLine.java deleted file mode 100644 index f7de33f..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/CodeLine.java +++ /dev/null @@ -1,28 +0,0 @@ -package nl.sander.beejava.apiv2; - - -public class CodeLine extends Instruction { - private final Opcode opcode; - private CodeContainer owner; - - public CodeLine(Opcode opcode, String operand) { - super(operand); - this.opcode = opcode; - } - - public String getOperand() { - return operand; - } - - public Opcode getOpcode() { - return opcode; - } - - public void setOwner(CodeContainer owner) { - this.owner = owner; - } - - public CodeContainer getOwner() { - return owner; - } -} diff --git a/src/main/java/nl/sander/beejava/apiv2/Opcode.java b/src/main/java/nl/sander/beejava/apiv2/Opcode.java deleted file mode 100644 index d2d1d20..0000000 --- a/src/main/java/nl/sander/beejava/apiv2/Opcode.java +++ /dev/null @@ -1,65 +0,0 @@ -package nl.sander.beejava.apiv2; - -import java.util.Optional; - -public enum Opcode { - LOAD, - STORE, - CONST, - RETURN, - ARRAYLENGTH, - THROW, - CAST, - ADD, - COMPARE, - DIVIDE, - MULTIPLY, - NEGATE, - REMAINDER, - SUBTRACT, - GET, - GOTO, - TO_DOUBLE, - TO_INT, - TO_LONG, - TO_BYTE, - TO_CHAR, - TO_FLOAT, - TO_SHORT, - AND, - IF_EQUAL, - IF_NOT_EQUAL, - IF_LESS_THAN, - IF_GREATER_OR_EQUAL, - IF_GREATER_THAN, - IF_LESS_OR_EQUAL, - IF_NOT_NULL, - IF_NULL, - INCREMENT, - INSTANCEOF, - INVOKE, - OR, - SHIFT_LEFT, - SHIFT_RIGHT, - LOGICAL_SHIFT_RIGHT, - XOR, - LOOKUPSWITCH, - MONITORENTER, - MONITOREXIT, - NEW, - NEWARRAY, - MULTIANEWARRAY, - PUT, - SWAP, - TABLESWITCH, - WIDE; - - static Optional get(String text) { - for (Opcode opcode : Opcode.values()) { - if (opcode.toString().equals(text)) { - return Optional.of(opcode); - } - } - return Optional.empty(); - } -} diff --git a/src/main/java/nl/sander/beejava/classinfo/Info.java b/src/main/java/nl/sander/beejava/classinfo/Info.java index 67a6a56..e114ce2 100644 --- a/src/main/java/nl/sander/beejava/classinfo/Info.java +++ b/src/main/java/nl/sander/beejava/classinfo/Info.java @@ -6,7 +6,7 @@ import nl.sander.beejava.constantpool.entry.Utf8Entry; import java.util.HashSet; import java.util.Set; -public abstract class Info { +public abstract class Info> { protected final Utf8Entry nameEntry; protected final Utf8Entry descriptorEntry; diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/ConstantPoolEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/ConstantPoolEntry.java index 0cced65..c399b72 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/ConstantPoolEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/ConstantPoolEntry.java @@ -29,10 +29,19 @@ public abstract class ConstantPoolEntry { return getBytes()[0]; } + /** + * The output of this ends up in the class file as the constantpool index of the entry. + * + * @return the cp index of the entry. + */ public int getIndex() { return index; } + /** + * With this the ConstantPoolCreator sets the calculated index. + * @param index the cp index to assign to the entry + */ public void setIndex(int index) { this.index = index; } @@ -61,8 +70,20 @@ public abstract class ConstantPoolEntry { return (byte) (u16 >>> 8); } - protected byte getByte(long bits, int positions) { - return (byte) ((bits >>> (positions * 8)) & 0xFF); + /** + * get the Nth byte in an "array" of bits encoded in a long (i64). Used to create the stream representation of 64bit numbers + * + * @param bits a long in which a long or a double is encoded. + * @param position the index of the byte (0..7) in the array of bits + * @return the Nth byte in the long + */ + protected byte getByte(long bits, int position) { + if (position > 0 && position < 8) { + return (byte) ((bits >>> (position * 8)) & 0xFF); + } else { + throw new IllegalArgumentException("position must be 0..7"); + } + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/FloatEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/FloatEntry.java index aa55016..a88e16c 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/FloatEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/FloatEntry.java @@ -7,13 +7,17 @@ public class FloatEntry extends LeafEntry { private final float floatVal; + public FloatEntry(String floatVal) { + this.floatVal = Float.parseFloat(floatVal); + } + public FloatEntry(float floatVal) { this.floatVal = floatVal; } @Override public String toString() { - return "Float\t\t" +floatVal; + return "Float\t\t" + floatVal; } @Override diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/IntegerEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/IntegerEntry.java index f7b21b5..af195ee 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/IntegerEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/IntegerEntry.java @@ -7,8 +7,12 @@ public class IntegerEntry extends LeafEntry { private final int intVal; - public IntegerEntry(int integer) { - this.intVal = integer; + public IntegerEntry(String integerVal) { + this.intVal = Integer.parseInt(integerVal); + } + + public IntegerEntry(int integerVal) { + this.intVal = integerVal; } @Override diff --git a/src/main/java/nl/sander/beejava/operands/ConstantOperand.java b/src/main/java/nl/sander/beejava/operands/ConstantOperand.java new file mode 100644 index 0000000..599a63b --- /dev/null +++ b/src/main/java/nl/sander/beejava/operands/ConstantOperand.java @@ -0,0 +1,22 @@ +package nl.sander.beejava.operands; + +import nl.sander.beejava.Primitive; + +public class ConstantOperand extends Operand { + private final Primitive type; + private final String value; + + + public ConstantOperand(Primitive type, String value) { + this.type = type; + this.value = value; + } + + public Primitive getType() { + return type; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/nl/sander/beejava/operands/FieldOperand.java b/src/main/java/nl/sander/beejava/operands/FieldOperand.java new file mode 100644 index 0000000..ca183e0 --- /dev/null +++ b/src/main/java/nl/sander/beejava/operands/FieldOperand.java @@ -0,0 +1,11 @@ +package nl.sander.beejava.operands; + +public class FieldOperand extends Operand{ + public final String className; + public final String fieldName; + + public FieldOperand(String className, String fieldName) { + this.className = className; + this.fieldName = fieldName; + } +} diff --git a/src/main/java/nl/sander/beejava/operands/LocalVariableOperand.java b/src/main/java/nl/sander/beejava/operands/LocalVariableOperand.java new file mode 100644 index 0000000..a82bc0d --- /dev/null +++ b/src/main/java/nl/sander/beejava/operands/LocalVariableOperand.java @@ -0,0 +1,9 @@ +package nl.sander.beejava.operands; + +public class LocalVariableOperand extends Operand{ + public final String name; + + public LocalVariableOperand(String name) { + this.name = name; + } +} diff --git a/src/main/java/nl/sander/beejava/operands/MethodOperand.java b/src/main/java/nl/sander/beejava/operands/MethodOperand.java new file mode 100644 index 0000000..48151df --- /dev/null +++ b/src/main/java/nl/sander/beejava/operands/MethodOperand.java @@ -0,0 +1,22 @@ +package nl.sander.beejava.operands; + +public class MethodOperand extends Operand{ + public final String className; + public final String methodName; + public final String signature; + + public MethodOperand(String className, String methodName, String signature) { + this.className = className; + this.methodName = methodName; + this.signature = signature; + } + + @Override + public String toString() { + return "MethodOperand{" + + "className='" + className + '\'' + + ", methodName='" + methodName + '\'' + + ", signature='" + signature + '\'' + + '}'; + } +} diff --git a/src/main/java/nl/sander/beejava/operands/Operand.java b/src/main/java/nl/sander/beejava/operands/Operand.java new file mode 100644 index 0000000..28fa969 --- /dev/null +++ b/src/main/java/nl/sander/beejava/operands/Operand.java @@ -0,0 +1,4 @@ +package nl.sander.beejava.operands; + +public abstract class Operand { +} diff --git a/src/main/java/nl/sander/beejava/operands/VoidOperand.java b/src/main/java/nl/sander/beejava/operands/VoidOperand.java new file mode 100644 index 0000000..d0fd866 --- /dev/null +++ b/src/main/java/nl/sander/beejava/operands/VoidOperand.java @@ -0,0 +1,9 @@ +package nl.sander.beejava.operands; + +public class VoidOperand extends Operand{ + public final static VoidOperand INSTANCE = new VoidOperand(); + + private VoidOperand() { + // + } +} diff --git a/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java b/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java index 43ca295..1745856 100644 --- a/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java +++ b/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java @@ -24,6 +24,6 @@ public class BytecodeGeneratorTests { @Test public void testFields() throws ClassNotFoundException { - BytecodeGenerator.generate(Compiler.compile(TestData.createClassWithField(int.class))); + BytecodeGenerator.generate(Compiler.compile(TestData.createClassWithField(Integer.class))); } } diff --git a/src/test/java/nl/sander/beejava/SourceCompilerTest.java b/src/test/java/nl/sander/beejava/SourceCompilerTest.java index 0a90911..4912e42 100644 --- a/src/test/java/nl/sander/beejava/SourceCompilerTest.java +++ b/src/test/java/nl/sander/beejava/SourceCompilerTest.java @@ -1,6 +1,9 @@ package nl.sander.beejava; -import nl.sander.beejava.apiv2.*; +import nl.sander.beejava.api.BeeField; +import nl.sander.beejava.api.BeeMethod; +import nl.sander.beejava.api.BeeParameter; +import nl.sander.beejava.api.BeeSource; import nl.sander.beejava.flags.AccessFlags; import nl.sander.beejava.flags.ClassAccessFlags; import nl.sander.beejava.flags.FieldAccessFlag; @@ -20,11 +23,16 @@ public class SourceCompilerTest { BeeSource beeSource = new SourceCompiler(TestData2.simpleBean).doCompile(); assertEquals("com.acme.SimpleBean", beeSource.getName()); assertEquals(ClassAccessFlags.SUPER.getBytecode() | ClassAccessFlags.PUBLIC.getBytecode(), AccessFlags.combine(beeSource.getAccessFlags())); - assertTrue(beeSource.getFields().contains(new BeeField(Set.of(FieldAccessFlag.PRIVATE), int.class, "value"))); + assertTrue(beeSource.getFields().contains(new BeeField(beeSource.getName(), Set.of(FieldAccessFlag.PRIVATE), int.class, "value"))); assertEquals(1, beeSource.getConstructors().size()); Set methods = beeSource.getMethods(); assertEquals(2, methods.size()); - assertTrue(methods.contains(new BeeMethod("getValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(), int.class, List.of()))); - assertTrue(methods.contains(new BeeMethod("setValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(new BeeParameter(int.class, "newValue")), Void.TYPE, List.of()))); + assertTrue(methods.contains(new BeeMethod(beeSource, "getValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(), int.class, List.of()))); + assertTrue(methods.contains(new BeeMethod(beeSource, "setValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(new BeeParameter(int.class, "newValue")), Void.TYPE, List.of()))); + + OpcodeTranslator.translate(beeSource); + + Compiler.compile(beeSource); + } } diff --git a/src/test/java/nl/sander/beejava/TestData.java b/src/test/java/nl/sander/beejava/TestData.java index 5879bb3..ff2c7b4 100644 --- a/src/test/java/nl/sander/beejava/TestData.java +++ b/src/test/java/nl/sander/beejava/TestData.java @@ -1,113 +1,38 @@ package nl.sander.beejava; -import nl.sander.beejava.api.*; -import nl.sander.beejava.flags.FieldAccessFlag; -import nl.sander.beejava.flags.MethodAccessFlag; - -import java.io.Serializable; - -import static nl.sander.beejava.api.CodeLine.line; -import static nl.sander.beejava.api.Opcode.*; -import static nl.sander.beejava.flags.ClassAccessFlags.PUBLIC; -import static nl.sander.beejava.flags.ClassAccessFlags.SUPER; +import nl.sander.beejava.api.BeeSource; public class TestData { - public static BeeSource createEmptyClass() throws ClassNotFoundException { - return BeeSource.builder() - .withClassFileVersion(Version.V14) - .withPackage("nl.sander.beejava.test") - .withAccessFlags(PUBLIC, SUPER) - .withSimpleName("EmptyBean") - .withSuperClass(Object.class) // Not mandatory, like in java sourcecode - .withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them - .build(); + public static BeeSource createEmptyClass() { + return SourceCompiler.compile(""" + class public nl.sander.beejava.test.EmptyBean + constructor public() + INVOKE this.super() + RETURN + """); } - public static BeeSource emptyClassWithInterface() throws ClassNotFoundException { - return BeeSource.builder() - .withClassFileVersion(Version.V14) - .withPackage("nl.sander.beejava.test") - .withAccessFlags(PUBLIC) - .withSimpleName("EmptyBean") - .withSuperClass(Object.class) // Not mandatory, like in java sourcecode - .withInterfaces(Serializable.class) - .withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them - .build(); + public static BeeSource emptyClassWithInterface() { + return SourceCompiler.compile(""" + class public nl.sander.beejava.test.EmptyBean implements java.io.Serializable + constructor public() + INVOKE this.super()V + RETURN + """); } - public static BeeSource createClassWithTwoReferencesToSomeClass() throws ClassNotFoundException { - BeeMethod print1 = BeeMethod.builder() - .withName("print1") - .withAccessFlags(MethodAccessFlag.PUBLIC) - .withCode( - line(GET, "java.lang.System","out"), - line(LD_CONST, "1"), - line(INVOKE, "java.io.PrintStream", "println", "java.lang.String"), - line(RETURN)) - .build(); + public static BeeSource createClassWithField(Class fieldType) { + String template = """ + class com.acme.SimpleBean(V15) + field private %s field + constructor public(%s arg) + INVOKE this.super()V + LOAD arg + PUT this.field + RETURN + """; -// INVOKE System.out.println("1") - - - BeeMethod print2 = BeeMethod.builder() - .withName("print2") - .withAccessFlags(MethodAccessFlag.PUBLIC) - .withCode( - line(GET, "java.lang.System","out"), - line(LD_CONST, "2"), - line(INVOKE, "java.io.PrintStream", "println", "java.lang.String"), - line(RETURN)) - .build(); - - return BeeSource.builder() - .withClassFileVersion(Version.V14) - .withPackage("nl.sander.beejava.test") - .withAccessFlags(PUBLIC, SUPER) - .withSimpleName("ClassWithReferences") - .withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them - .withMethods(print1, print2) - .build(); + return SourceCompiler.compile(String.format(template, fieldType.getName(), fieldType.getName())); } - public static BeeSource createClassWithField(Class fieldType) throws ClassNotFoundException { - BeeField field = BeeField.builder() - .withAccessFlags(FieldAccessFlag.PRIVATE) - .withType(fieldType) - .withName("field") - .build(); - - BeeParameter parameter = BeeParameter.create(fieldType, "value"); - - BeeConstructor constructor = BeeConstructor.builder() - .withAccessFlags(MethodAccessFlag.PUBLIC) - .withFormalParameters(parameter) - .withCode( - line(LD_VAR, Ref.THIS), - line(INVOKE, Ref.SUPER, "", "()"), - line(LD_VAR, Ref.THIS), - line(LD_VAR, parameter), - line(PUT, field), - line(RETURN)) - .build(); - - return BeeSource.builder() - .withClassFileVersion(Version.V14) - .withPackage("nl.sander.beejava.test") - .withAccessFlags(PUBLIC) - .withSimpleName("Bean") - .withSuperClass(Object.class) - .withFields(field) - .withConstructors(constructor) - .build(); - } - - public static BeeConstructor createDefaultConstructor() throws ClassNotFoundException { - return BeeConstructor.builder() - .withAccessFlags(MethodAccessFlag.PUBLIC) - .withCode( - line(LD_VAR, Ref.THIS), - line(INVOKE, Ref.SUPER, "", "()"), - line(RETURN)) - .build(); - } } diff --git a/src/test/java/nl/sander/beejava/TestData2.java b/src/test/java/nl/sander/beejava/TestData2.java index 2961afd..82c5dd7 100644 --- a/src/test/java/nl/sander/beejava/TestData2.java +++ b/src/test/java/nl/sander/beejava/TestData2.java @@ -3,10 +3,10 @@ package nl.sander.beejava; public class TestData2 { public final static String simpleBean= """ - class com.acme.SimpleBean(V15) + class public com.acme.SimpleBean(V15) field private int value constructor public() - INVOKE super() + INVOKE this.super() RETURN method public getValue() -> int RETURN this.value @@ -40,7 +40,7 @@ public class TestData2 { String constructor_opcope = """ constructor nl.sander.beejava.Compiler(nl.sander.beejava.CompiledClass arg_0); - INVOKE super() + INVOKE this.super() NEW nl.sander.beejava.ConstantPoolCreator() PUT this.constantPoolCreator diff --git a/src/test/java/nl/sander/beejava/TypeMapperTest.java b/src/test/java/nl/sander/beejava/TypeMapperTest.java index 519bc45..c877047 100644 --- a/src/test/java/nl/sander/beejava/TypeMapperTest.java +++ b/src/test/java/nl/sander/beejava/TypeMapperTest.java @@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.fail; public class TypeMapperTest { @Test - public void test_int() throws ClassNotFoundException { + public void test_int() { // Arrange BeeSource beeSource = TestData.createClassWithField(int.class); @@ -24,7 +24,7 @@ public class TypeMapperTest { } @Test - public void test_double() throws ClassNotFoundException { + public void test_double() { // Arrange BeeSource beeSource = TestData.createClassWithField(double.class); @@ -37,7 +37,7 @@ public class TypeMapperTest { } @Test - public void test_float() throws ClassNotFoundException { + public void test_float() { // Arrange BeeSource beeSource = TestData.createClassWithField(float.class); @@ -50,7 +50,7 @@ public class TypeMapperTest { } @Test - public void test_byte() throws ClassNotFoundException { + public void test_byte() { // Arrange BeeSource beeSource = TestData.createClassWithField(byte.class); @@ -63,7 +63,7 @@ public class TypeMapperTest { } @Test - public void test_short() throws ClassNotFoundException { + public void test_short() { // Arrange BeeSource beeSource = TestData.createClassWithField(short.class); @@ -76,7 +76,7 @@ public class TypeMapperTest { } @Test - public void test_long() throws ClassNotFoundException { + public void test_long() { // Arrange BeeSource beeSource = TestData.createClassWithField(long.class); @@ -89,7 +89,7 @@ public class TypeMapperTest { } @Test - public void test_char() throws ClassNotFoundException { + public void test_char() { // Arrange BeeSource beeSource = TestData.createClassWithField(char.class); @@ -102,7 +102,7 @@ public class TypeMapperTest { } @Test - public void test_boolean() throws ClassNotFoundException { + public void test_boolean() { // Arrange BeeSource beeSource = TestData.createClassWithField(boolean.class); @@ -115,7 +115,7 @@ public class TypeMapperTest { } @Test - public void test_Object() throws ClassNotFoundException { + public void test_Object() { // Arrange BeeSource beeSource = TestData.createClassWithField(Object.class); @@ -128,7 +128,7 @@ public class TypeMapperTest { } @Test - public void test_int_array() throws ClassNotFoundException { + public void test_int_array() { // Arrange BeeSource beeSource = TestData.createClassWithField(int[].class); @@ -141,7 +141,7 @@ public class TypeMapperTest { } @Test - public void test_Object_array() throws ClassNotFoundException { + public void test_Object_array() { // Arrange BeeSource beeSource = TestData.createClassWithField(String[].class); diff --git a/src/test/java/nl/sander/beejava/constantpool/entry/TagCorrectnessTest.java b/src/test/java/nl/sander/beejava/constantpool/entry/TagCorrectnessTest.java index 6d4e606..90f3e1d 100644 --- a/src/test/java/nl/sander/beejava/constantpool/entry/TagCorrectnessTest.java +++ b/src/test/java/nl/sander/beejava/constantpool/entry/TagCorrectnessTest.java @@ -12,7 +12,7 @@ public class TagCorrectnessTest { public void testSpec() { assertEquals(1, utf8().getTag()); assertEquals(3, new IntegerEntry(0).getTag()); - assertEquals(4, new FloatEntry(0).getTag()); + assertEquals(4, new FloatEntry(0F).getTag()); assertEquals(5, new LongEntry(0).getTag()); assertEquals(6, new DoubleEntry(0).getTag()); assertEquals(7, classEntry().getTag()); diff --git a/src/test/java/nl/sander/beejava/e2e/BeanWithMethodsTest.java b/src/test/java/nl/sander/beejava/e2e/BeanWithMethodsTest.java index 905303f..ba6a6a2 100644 --- a/src/test/java/nl/sander/beejava/e2e/BeanWithMethodsTest.java +++ b/src/test/java/nl/sander/beejava/e2e/BeanWithMethodsTest.java @@ -1,9 +1,7 @@ package nl.sander.beejava.e2e; -import nl.sander.beejava.BytecodeGenerator; -import nl.sander.beejava.CompiledClass; import nl.sander.beejava.Compiler; -import nl.sander.beejava.TestData; +import nl.sander.beejava.*; import nl.sander.beejava.api.BeeSource; import org.junit.jupiter.api.Test; @@ -13,36 +11,52 @@ import java.lang.reflect.Modifier; import static org.junit.jupiter.api.Assertions.*; /** - * A rather simple case, still meaningful nonetheless. - * Green means class can be loaded, so the class file structure is valid. - * The EmptyBean class just contains a default constructor. + * Proves ability to compile opcodes for GET, LOAD, INVOKE, RETURN. + * Secondly the usage of method refs, constants. */ public class BeanWithMethodsTest { @Test public void testEmptyBean() throws Exception { // Arrange - BeeSource emptyClass = TestData.createClassWithTwoReferencesToSomeClass(); + BeeSource printer = SourceCompiler.compile(""" + class nl.sander.beejava.test.ClassWithReferences + constructor public() + INVOKE this.super()V + RETURN + method public print1() + GET java.lang.System.out + LOAD "1" + INVOKE java/io/PrintStream.println(Ljava/lang/String;)V + RETURN + method public print2() + GET java.lang.System.out + LOAD int 2 + INVOKE java/io/PrintStream.println(I)V + RETURN + """); // Act - CompiledClass compiledClass = Compiler.compile(emptyClass); + OpcodeTranslator.translate(printer); + CompiledClass compiledClass = Compiler.compile(printer); byte[] bytecode = BytecodeGenerator.generate(compiledClass); ByteClassLoader classLoader = new ByteClassLoader(); - classLoader.setByteCode("nl.sander.beejava.test.ClassWithReferences", bytecode); + String className = "nl.sander.beejava.test.ClassWithReferences"; + classLoader.setByteCode(className, bytecode); // Assert - Class classWithReferences = classLoader.loadClass("nl.sander.beejava.test.ClassWithReferences"); + Class classWithReferences = classLoader.loadClass(className); assertNotNull(classWithReferences); Method[] methods = classWithReferences.getDeclaredMethods(); assertEquals(2, methods.length); assertEquals("print1", methods[0].getName()); assertTrue(Modifier.isPublic(methods[0].getModifiers())); - assertEquals(0,methods[0].getParameterCount()); + assertEquals(0, methods[0].getParameterCount()); assertEquals("print2", methods[1].getName()); // ordering may cause failures assertTrue(Modifier.isPublic(methods[1].getModifiers())); - assertEquals(0,methods[1].getParameterCount()); + assertEquals(0, methods[1].getParameterCount()); } } diff --git a/src/test/java/nl/sander/beejava/e2e/EmptyBeanTest.java b/src/test/java/nl/sander/beejava/e2e/EmptyBeanTest.java index d9e5e41..a26658e 100644 --- a/src/test/java/nl/sander/beejava/e2e/EmptyBeanTest.java +++ b/src/test/java/nl/sander/beejava/e2e/EmptyBeanTest.java @@ -1,37 +1,26 @@ package nl.sander.beejava.e2e; -import nl.sander.beejava.BytecodeGenerator; -import nl.sander.beejava.CompiledClass; -import nl.sander.beejava.Compiler; -import nl.sander.beejava.api.BeeConstructor; -import nl.sander.beejava.api.BeeSource; -import nl.sander.beejava.api.Ref; -import nl.sander.beejava.api.Version; -import nl.sander.beejava.flags.MethodAccessFlag; +import nl.sander.beejava.OverallCompiler; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; -import static nl.sander.beejava.api.CodeLine.line; -import static nl.sander.beejava.api.Opcode.*; -import static nl.sander.beejava.flags.ClassAccessFlags.PUBLIC; -import static nl.sander.beejava.flags.ClassAccessFlags.SUPER; import static org.junit.jupiter.api.Assertions.assertNotNull; /** - * A rather simple case, still meaningful nonetheless. - * Green means class can be loaded, so the class file structure is valid. - * The EmptyBean class just contains a default constructor. + * Hello world test for bejava. Proves ability to compile bejava-lang to valid bytecode. */ public class EmptyBeanTest { @Test public void testEmptyBean() throws Exception { - // Arrange - BeeSource emptyClass = createEmptyClass(); - // Act - CompiledClass compiledClass = Compiler.compile(emptyClass); - byte[] bytecode = BytecodeGenerator.generate(compiledClass); + byte[] bytecode = OverallCompiler.compile(""" + class public nl.sander.beejava.test.EmptyBean + constructor public() + INVOKE this.super()V + RETURN + """); + ByteClassLoader classLoader = new ByteClassLoader(); classLoader.setByteCode("nl.sander.beejava.test.EmptyBean", bytecode); @@ -42,27 +31,5 @@ public class EmptyBeanTest { Object instance = constructor.newInstance(); assertNotNull(instance); - - } - - private BeeSource createEmptyClass() throws ClassNotFoundException { - return BeeSource.builder() - .withClassFileVersion(Version.V14) - .withPackage("nl.sander.beejava.test") - .withAccessFlags(PUBLIC, SUPER) - .withSimpleName("EmptyBean") - .withSuperClass(Object.class) // Not mandatory, like in java sourcecode - .withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them - .build(); - } - - private BeeConstructor createDefaultConstructor() throws ClassNotFoundException { - return BeeConstructor.builder() - .withAccessFlags(MethodAccessFlag.PUBLIC) - .withCode( - line(LD_VAR, Ref.THIS), - line(INVOKE, Ref.SUPER, "", "()"), - line(RETURN)) - .build(); } }