diff --git a/src/main/java/nl/sander/beejava/Compiler.java b/src/main/java/nl/sander/beejava/Compiler.java index 029b94b..d47c6c2 100644 --- a/src/main/java/nl/sander/beejava/Compiler.java +++ b/src/main/java/nl/sander/beejava/Compiler.java @@ -34,6 +34,7 @@ public class Compiler { CompiledClass compile() { compiledClass.getBeeClass().getConstructors().forEach(this::updateConstantPool); + compiledClass.getBeeClass().getMethods().forEach(this::updateConstantPool); // TODO update constant pool for fields ? // TODO update constant pool for methods @@ -45,7 +46,7 @@ public class Compiler { } - private void updateConstantPool(ContainsCode codeContainer) { + private void updateConstantPool(CodeContainer codeContainer) { codeContainer.getCode().forEach(this::updateConstantPool); } @@ -59,12 +60,16 @@ public class Compiler { * So Compiled class also contains a set of unique root nodes. */ private void updateConstantPool(CodeLine codeline) { - if (codeline.hasMethod()) { + if (codeline.hasMethodCall()) { compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateMethodRefEntry(codeline)); } - if (codeline.hasField()) { + if (codeline.hasRefToOwnField() || codeline.hasRefToExternalField()) { compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateFieldRefEntry(codeline)); } + + if (codeline.hasConstValue()) { + compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreatePrimitiveEntry(codeline)); + } } } diff --git a/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java b/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java index 7339ed3..ece8fdf 100644 --- a/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java +++ b/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java @@ -1,6 +1,5 @@ package nl.sander.beejava; -import nl.sander.beejava.api.BeeClass; import nl.sander.beejava.api.CodeLine; import nl.sander.beejava.api.Ref; import nl.sander.beejava.constantpool.entry.*; @@ -31,7 +30,16 @@ class ConstantPoolEntryCreator { } private NameAndTypeEntry createFieldNameAndType(CodeLine codeline) { - return cache(new NameAndTypeEntry(cache(new Utf8Entry(codeline.getField().getName())), cache(new Utf8Entry(TypeMapper.map(codeline.getField().getType()))))); + 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 { + return cache(new NameAndTypeEntry( + cache(new Utf8Entry(codeline.getExternalfield())), + cache(new Utf8Entry("L" + codeline.getExternalfieldClass())) + )); + } } private NameAndTypeEntry getOrCreateMethodNameAndType(CodeLine codeline) { @@ -41,21 +49,26 @@ class ConstantPoolEntryCreator { } private ClassEntry getOrCreateClassEntry(CodeLine codeline) { - if (codeline.getRef() == Ref.SUPER) { //this and super are rather special - ClassEntry superClass = getClassEntry(compiledClass.getBeeClass().getSuperClass().getName()); - compiledClass.setSuperClass(superClass); - return superClass; + if (codeline.hasRef()) { + if (codeline.getRef() == Ref.SUPER) { //this and super are rather special + ClassEntry superClass = getClassEntry(compiledClass.getBeeClass().getSuperClass().getName()); + compiledClass.setSuperClass(superClass); + return superClass; - } else if (codeline.getRef() == Ref.THIS) { - addThisClass(); - return compiledClass.getThisClass(); + } else if (codeline.getRef() == Ref.THIS) { + addThisClass(); + return compiledClass.getThisClass(); + } + } else if (codeline.hasClassName()) { + return getClassEntry(codeline.getClassName()); } - //TODO other cases + throw new RuntimeException("shouldn't be here"); } - void addThisClass(){ + void addThisClass() { ClassEntry classEntry = getClassEntry(compiledClass.getBeeClass().getName()); + compiledClass.addConstantPoolEntry(classEntry); compiledClass.setThisClass(classEntry); } @@ -71,7 +84,22 @@ class ConstantPoolEntryCreator { }); } + 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 RuntimeException(); // TODO find out why are you here + } public void addFields() { compiledClass.getBeeClass().getFields().forEach(field -> { @@ -88,9 +116,11 @@ class ConstantPoolEntryCreator { // First create an object using the supplier, but if it's found in cache, return the cached entry and discard the first. // Can't check for equality unless you create a potential new entry first int hash = newEntry.hashCode(); - return (T) cache.computeIfAbsent(hash, k -> newEntry); + T a = (T) cache.computeIfAbsent(hash, k -> newEntry); + return a; // a hashmap with key hash of value is weird right? // A HashSet is a HashMap with entry: key = value, which would work, but I cannot _get_ anything from a set. } + } diff --git a/src/main/java/nl/sander/beejava/api/BeeClass.java b/src/main/java/nl/sander/beejava/api/BeeClass.java index ec2f161..d4877b9 100644 --- a/src/main/java/nl/sander/beejava/api/BeeClass.java +++ b/src/main/java/nl/sander/beejava/api/BeeClass.java @@ -2,6 +2,7 @@ package nl.sander.beejava.api; import nl.sander.beejava.flags.ClassAccessFlag; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.Set; @@ -15,10 +16,11 @@ public class BeeClass { 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 BeeClass(Version classFileVersion, BeePackage beePackage, Set accessFlags, String simpleName, Class superClass, - Set> interfaces, Set fields, Set constructors) { + Set> interfaces, Set fields, Set constructors, Set methods) { this.classFileVersion = classFileVersion; this.beePackage = beePackage; this.accessFlags.addAll(accessFlags); @@ -27,6 +29,7 @@ public class BeeClass { this.interfaces.addAll(interfaces); this.fields.addAll(fields); this.constructors.addAll(constructors); + this.methods.addAll(methods); } public static BeeClass.Builder builder() { @@ -52,6 +55,10 @@ public class BeeClass { return constructors; } + public Set getMethods() { + return methods; + } + public Set getAccessFlags() { return accessFlags; } @@ -79,11 +86,12 @@ public class BeeClass { 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 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() { } @@ -128,10 +136,14 @@ public class BeeClass { return this; } - public BeeClass build() { - return new BeeClass(version, beePackage, accessFlags, simpleName, superClass, interfaces, fields, constructors); + public Builder withMethods(BeeMethod... methods) { + this.methods.addAll(Arrays.asList(methods)); + return this; } + public BeeClass build() { + return new BeeClass(version, beePackage, accessFlags, simpleName, superClass, interfaces, fields, constructors, methods); + } } } diff --git a/src/main/java/nl/sander/beejava/api/BeeConstructor.java b/src/main/java/nl/sander/beejava/api/BeeConstructor.java index f5d7643..a28355f 100644 --- a/src/main/java/nl/sander/beejava/api/BeeConstructor.java +++ b/src/main/java/nl/sander/beejava/api/BeeConstructor.java @@ -1,6 +1,6 @@ package nl.sander.beejava.api; -import nl.sander.beejava.ContainsCode; +import nl.sander.beejava.CodeContainer; import nl.sander.beejava.flags.MethodAccessFlag; import java.util.*; @@ -8,17 +8,16 @@ import java.util.*; /** * Models a constructor */ -public class BeeConstructor implements ContainsCode { +public class BeeConstructor extends CodeContainer { private final Set accessFlags = new HashSet<>(); private final Set formalParameters = new HashSet<>(); - private final List code = new LinkedList<>(); private BeeConstructor(Set accessFlags, List formalParameters, List code) { this.formalParameters.addAll(formalParameters); this.accessFlags.addAll(accessFlags); - this.code.addAll(code); + super.code.addAll(code); } public static Builder builder() { @@ -33,11 +32,6 @@ public class BeeConstructor implements ContainsCode { return formalParameters; } - @Override - public List getCode() { - return code; - } - @Override public String toString() { return "BeeConstructor{" + diff --git a/src/main/java/nl/sander/beejava/api/BeeMethod.java b/src/main/java/nl/sander/beejava/api/BeeMethod.java new file mode 100644 index 0000000..400fd66 --- /dev/null +++ b/src/main/java/nl/sander/beejava/api/BeeMethod.java @@ -0,0 +1,59 @@ +package nl.sander.beejava.api; + +import nl.sander.beejava.CodeContainer; +import nl.sander.beejava.flags.MethodAccessFlag; + +import java.util.*; + +public final class BeeMethod extends CodeContainer { + private final Set accessFlags = new HashSet<>(); + private final Set formalParameters = new HashSet<>(); + private final Class returnType; + + private BeeMethod(Set accessFlags, + List formalParameters, + Class returnType, List code) { + this.accessFlags.addAll(accessFlags); + this.formalParameters.addAll(formalParameters); + this.returnType = returnType; + super.code.addAll(code); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private final Set accessFlags = new HashSet<>(); + private final List formalParameters = new LinkedList<>(); + private final List code = new LinkedList<>(); + private Class returnType; + + private Builder() { + } + + 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() { + return new BeeMethod(accessFlags, formalParameters, returnType, code); + } + } +} diff --git a/src/main/java/nl/sander/beejava/api/CodeLine.java b/src/main/java/nl/sander/beejava/api/CodeLine.java index 92fcb88..ca83150 100644 --- a/src/main/java/nl/sander/beejava/api/CodeLine.java +++ b/src/main/java/nl/sander/beejava/api/CodeLine.java @@ -6,10 +6,15 @@ public class CodeLine { private final Opcode opcode; private Ref ref; private BeeParameter parameter; + private String className; private String methodName; private String inputSignature; private String outputSignature; - private BeeField field; + private BeeField ownfield; // when you create a class with a field, you can refer to it + private String externalfield; // when you refer to a field from another class + + private Object constValue; + private String externalFieldType; CodeLine(int linenumber, Opcode opcode) { this.linenumber = linenumber; @@ -20,6 +25,18 @@ public class CodeLine { return new CodeLine(nr, opcode).withRef(ref); } + public static CodeLine line(int nr, Opcode opcode, String fieldClass, String fieldName) { + return new CodeLine(nr, opcode).withExternalFieldRef(fieldClass, fieldName); + } + + public static CodeLine line(int nr, Opcode opcode, Object constValue) { + return new CodeLine(nr, opcode).withConstValue(constValue); + } + + public static CodeLine line(int nr, Opcode opcode, String className, String methodName, String inputSignature) { + return new CodeLine(nr, opcode).withClassName(className).withMethodName(methodName).withInput(inputSignature).withVoidOutput(); + } + public static CodeLine line(int nr, Opcode opcode, Ref ref, String methodNameRef, String inputSignature) { return new CodeLine(nr, opcode).withRef(ref).withMethodName(methodNameRef).withInput(inputSignature).withVoidOutput(); } @@ -37,7 +54,7 @@ public class CodeLine { } public static CodeLine line(int nr, Opcode opcode, BeeField intField) { - return new CodeLine(nr, opcode).withRef(Ref.THIS).withField(intField); + return new CodeLine(nr, opcode).withRef(Ref.THIS).withOwnField(intField); } private CodeLine withRef(Ref ref) { @@ -45,6 +62,11 @@ public class CodeLine { return this; } + private CodeLine withClassName(String className) { + this.className = className; + return this; + } + private CodeLine withMethodName(String methodName) { this.methodName = methodName; return this; @@ -69,16 +91,57 @@ public class CodeLine { return this; } - private CodeLine withField(BeeField field) { - this.field = field; + 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.className = className; + this.externalFieldType = getType(className, field); + this.externalfield = 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 String getType(String className, String field) { + try { + return Class.forName(className).getField(field).getType().getName(); + } catch (ClassNotFoundException | NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + public boolean hasClassName() { + return className != null; + } + + + public String getClassName() { + return className; + } + public String getMethodName() { return methodName; } - public boolean hasMethod() { + public boolean hasMethodCall() { return methodName != null; } @@ -94,12 +157,21 @@ public class CodeLine { return ref != null; } - public boolean hasField() { - return field != null; + public boolean hasRefToOwnField() { + return ownfield != null; } - public BeeField getField() { - return field; + + public Object getConstValue() { + return constValue; + } + + public boolean hasConstValue() { + return constValue != null; + } + + public BeeField getOwnfield() { + return ownfield; } public BeeParameter getParameter() { @@ -107,4 +179,15 @@ public class CodeLine { } + public boolean hasRefToExternalField() { + return externalfield != null; + } + + public String getExternalfield() { + return externalfield; + } + + public String getExternalfieldClass() { + return externalFieldType; + } } diff --git a/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java b/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java index 554872d..7e816e6 100644 --- a/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java +++ b/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java @@ -1,12 +1,19 @@ package nl.sander.beejava.testclasses; +// two references to System. System should not be in the constant pool twice. public class BeanWithClassReferences { - public void print1(){ + public void print1() { System.out.println("1"); } - public void print2(){ - System.out.println("2"); + public void print2() { + System.out.println(create2()); + } + + public String create2() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("2"); + return stringBuilder.toString(); } }