diff --git a/javap/nl.sander.beejava.testclasses.BeanWithClassReferences_javap.txt b/javap/nl.sander.beejava.testclasses.BeanWithClassReferences_javap.txt new file mode 100644 index 0000000..6708d01 --- /dev/null +++ b/javap/nl.sander.beejava.testclasses.BeanWithClassReferences_javap.txt @@ -0,0 +1,93 @@ +Classfile /Users/Shautvast/IdeaProjects/beejava/target/test-classes/nl/sander/beejava/testclasses/BeanWithClassReferences.class + Last modified 10 Nov 2020; size 655 bytes + SHA-256 checksum 1bb4b80be80051827e68106c7ec72140696f105df82dfc28147eaaff73a9b667 + Compiled from "BeanWithClassReferences.java" +public class nl.sander.beejava.testclasses.BeanWithClassReferences + minor version: 0 + major version: 58 + flags: (0x0021) ACC_PUBLIC, ACC_SUPER + this_class: #23 // nl/sander/beejava/testclasses/BeanWithClassReferences + super_class: #2 // java/lang/Object + interfaces: 0, fields: 0, methods: 3, attributes: 1 +Constant pool: + #1 = Methodref #2.#3 // java/lang/Object."":()V + #2 = Class #4 // java/lang/Object + #3 = NameAndType #5:#6 // "":()V + #4 = Utf8 java/lang/Object + #5 = Utf8 + #6 = Utf8 ()V + #7 = Fieldref #8.#9 // java/lang/System.out:Ljava/io/PrintStream; + #8 = Class #10 // java/lang/System + #9 = NameAndType #11:#12 // out:Ljava/io/PrintStream; + #10 = Utf8 java/lang/System + #11 = Utf8 out + #12 = Utf8 Ljava/io/PrintStream; + #13 = String #14 // 1 + #14 = Utf8 1 + #15 = Methodref #16.#17 // java/io/PrintStream.println:(Ljava/lang/String;)V + #16 = Class #18 // java/io/PrintStream + #17 = NameAndType #19:#20 // println:(Ljava/lang/String;)V + #18 = Utf8 java/io/PrintStream + #19 = Utf8 println + #20 = Utf8 (Ljava/lang/String;)V + #21 = String #22 // 2 + #22 = Utf8 2 + #23 = Class #24 // nl/sander/beejava/testclasses/BeanWithClassReferences + #24 = Utf8 nl/sander/beejava/testclasses/BeanWithClassReferences + #25 = Utf8 Code + #26 = Utf8 LineNumberTable + #27 = Utf8 LocalVariableTable + #28 = Utf8 this + #29 = Utf8 Lnl/sander/beejava/testclasses/BeanWithClassReferences; + #30 = Utf8 print1 + #31 = Utf8 print2 + #32 = Utf8 SourceFile + #33 = Utf8 BeanWithClassReferences.java +{ + public nl.sander.beejava.testclasses.BeanWithClassReferences(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=1, locals=1, args_size=1 + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."":()V + 4: return + LineNumberTable: + line 3: 0 + LocalVariableTable: + Start Length Slot Name Signature + 0 5 0 this Lnl/sander/beejava/testclasses/BeanWithClassReferences; + + public void print1(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; + 3: ldc #13 // String 1 + 5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + 8: return + LineNumberTable: + line 6: 0 + line 7: 8 + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lnl/sander/beejava/testclasses/BeanWithClassReferences; + + public void print2(); + descriptor: ()V + flags: (0x0001) ACC_PUBLIC + Code: + stack=2, locals=1, args_size=1 + 0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream; + 3: ldc #21 // String 2 + 5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V + 8: return + LineNumberTable: + line 10: 0 + line 11: 8 + LocalVariableTable: + Start Length Slot Name Signature + 0 9 0 this Lnl/sander/beejava/testclasses/BeanWithClassReferences; +} +SourceFile: "BeanWithClassReferences.java" \ No newline at end of file diff --git a/pom.xml b/pom.xml index b865ad5..63af329 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,15 @@ org.apache.maven.plugins - maven-bytecodeGenerator-plugin + maven-compiler-plugin + + 15 + 15 + + + + org.apache.maven.plugins + maven-compiler-plugin 14 14 @@ -21,8 +29,8 @@ jar - 15 - 15 + 14 + 14 UTF-8 diff --git a/src/main/java/nl/sander/beejava/BytecodeGenerator.java b/src/main/java/nl/sander/beejava/BytecodeGenerator.java index e0f60be..6540616 100644 --- a/src/main/java/nl/sander/beejava/BytecodeGenerator.java +++ b/src/main/java/nl/sander/beejava/BytecodeGenerator.java @@ -8,31 +8,30 @@ import nl.sander.beejava.util.ByteBuf; public class BytecodeGenerator { - private final BeeClass beeClass; // maybe not a member? - private final Compiler compiler = new Compiler(); + private final CompiledClass compiledClass; // maybe not a member? private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator(); - public BytecodeGenerator(BeeClass beeClass) { - this.beeClass = beeClass; + public BytecodeGenerator(CompiledClass compiledClass) { + this.compiledClass = compiledClass; } - public static byte[] compile(BeeClass beeClass) { - BytecodeGenerator bytecodeGenerator = new BytecodeGenerator(beeClass); - return bytecodeGenerator.doCompile(); + public static byte[] generate(CompiledClass compiledClass) { + BytecodeGenerator bytecodeGenerator = new BytecodeGenerator(compiledClass); + return bytecodeGenerator.generate(); } - private byte[] doCompile() { + private byte[] generate() { ByteBuf buf = new ByteBuf(); buf.addU8(0xCA, 0xFE, 0xBA, 0xBE); - buf.addU16(beeClass.getClassFileVersion().getMinor()); - buf.addU16(beeClass.getClassFileVersion().getMajor()); + buf.addU16(compiledClass.getBeeClass().getClassFileVersion().getMinor()); + buf.addU16(compiledClass.getBeeClass().getClassFileVersion().getMajor()); + - CompiledClass compiledClass = compiler.compile(beeClass); ConstantPool constantPool = constantPoolCreator.createConstantPool(compiledClass.getConstantTree()); buf.addU16(constantPool.getLength()); buf.addU8(constantPool.getBytes()); - buf.addU16(ClassAccessFlag.getSum(beeClass.getAccessFlags())); + buf.addU16(ClassAccessFlag.getSum(compiledClass.getBeeClass().getAccessFlags())); buf.addU16(compiledClass.geThisIndex()); buf.addU16(compiledClass.getSuperIndex()); buf.addU16(compiledClass.getInterfaces().size()); diff --git a/src/main/java/nl/sander/beejava/CompiledClass.java b/src/main/java/nl/sander/beejava/CompiledClass.java index c6cec76..7d31cd3 100644 --- a/src/main/java/nl/sander/beejava/CompiledClass.java +++ b/src/main/java/nl/sander/beejava/CompiledClass.java @@ -3,80 +3,63 @@ package nl.sander.beejava; import nl.sander.beejava.api.BeeClass; import nl.sander.beejava.constantpool.entry.ClassEntry; import nl.sander.beejava.constantpool.entry.ConstantPoolEntry; -import nl.sander.beejava.constantpool.entry.Utf8Entry; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; -public class CompiledClass { +class CompiledClass { private final Set constantTree = new LinkedHashSet<>(); private final Set interfaces = new HashSet<>(); private final BeeClass beeClass; private ClassEntry thisClass; private ClassEntry superClass; - public CompiledClass(BeeClass beeClass) { + CompiledClass(BeeClass beeClass) { this.beeClass = beeClass; } - public int getSuperIndex() { + int getSuperIndex() { return superClass.getIndex(); } - public int geThisIndex() { + int geThisIndex() { return thisClass.getIndex(); } - public ClassEntry addSuperClass() { + void setSuperClass(ClassEntry newSuperClass) { if (superClass == null) { - superClass = createClassEntry(beeClass.getSuperClass().getName()); + superClass = newSuperClass; } - return superClass; } - public ClassEntry addThisClass() { - if (thisClass == null) { - thisClass = createClassEntry(internalName(beeClass.getName())); - } - return thisClass; - } - - public Set getConstantTree() { + Set getConstantTree() { return constantTree; } - public Set getInterfaces() { + Set getInterfaces() { return interfaces; } - public void addInterface(ClassEntry interfaceEntry) { + void addConstantPoolEntry(ConstantPoolEntry entry) { + constantTree.add(entry); // no duplicates + } + + void addInterface(ClassEntry interfaceEntry) { interfaces.add(interfaceEntry); } - public void addConstantPoolEntry(ConstantPoolEntry interfaceEntry) { - constantTree.add(interfaceEntry); + BeeClass getBeeClass() { + return beeClass; } - public void addInterfaces() { - beeClass.getInterfaces().forEach(interfase -> { - ClassEntry interfaceEntry = new ClassEntry(new Utf8Entry(internalName(interfase.getName()))); - addInterface(interfaceEntry); - addConstantPoolEntry(interfaceEntry); - }); + ClassEntry getThisClass() { + return thisClass; } - public void addFields() { - beeClass.getFields().forEach(field -> { - // TODO - }); - } - - private ClassEntry createClassEntry(String name) { - return new ClassEntry(new Utf8Entry(internalName(name))); - } - - private String internalName(String name) { - return name.replaceAll("\\.", "/"); + public void setThisClass(ClassEntry newThisClass) { + if (thisClass == null) { + thisClass = newThisClass; + } } } diff --git a/src/main/java/nl/sander/beejava/Compiler.java b/src/main/java/nl/sander/beejava/Compiler.java index 34d3f63..029b94b 100644 --- a/src/main/java/nl/sander/beejava/Compiler.java +++ b/src/main/java/nl/sander/beejava/Compiler.java @@ -2,8 +2,6 @@ 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.*; /** * Builds a set of a tree of constant pool entries that refer to each other. @@ -15,7 +13,13 @@ import nl.sander.beejava.constantpool.entry.*; * also TODO make sure entries aren't duplicates */ public class Compiler { - private CompiledClass compiledClass; + private final CompiledClass compiledClass; + private final ConstantPoolEntryCreator constantPoolEntryCreator; + + Compiler(CompiledClass compiledClass) { + this.compiledClass = compiledClass; + this.constantPoolEntryCreator = new ConstantPoolEntryCreator(compiledClass); + } /** * Creates a Set of nested entries that make up a single reference. For instance a class reference whose name is a utf8 reference. @@ -24,15 +28,18 @@ public class Compiler { * @param beeClass the Class object for which the constant pool needs to be created * @return a Set of constant pool entries */ - public CompiledClass compile(BeeClass beeClass) { - compiledClass = new CompiledClass(beeClass); - beeClass.getConstructors().forEach(this::updateConstantPool); + public static CompiledClass compile(BeeClass beeClass) { + return new Compiler(new CompiledClass(beeClass)).compile(); + } + + CompiledClass compile() { + compiledClass.getBeeClass().getConstructors().forEach(this::updateConstantPool); // TODO update constant pool for fields ? // TODO update constant pool for methods - compiledClass.addThisClass(); - compiledClass.addInterfaces(); - compiledClass.addFields(); + constantPoolEntryCreator.addThisClass(); + constantPoolEntryCreator.addInterfaces(); + constantPoolEntryCreator.addFields(); return compiledClass; } @@ -43,43 +50,21 @@ public class Compiler { } /* - * scan code line for items that need adding to the constant pool + * Scan code line for items that need adding to the constant pool + * + * Constantpool uniqueness is maintained in two ways: + * 1. ConstantPoolEntryCreator.getOrCreateMethodRefEntry (and other methods) return a tree of entries. + * Sub-entries are kept in cache by the ConstantPoolEntryCreator and it returns a cached entry over a new one, if deemed equal. + * 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.hasMethod()) { - addMethodToConstantPool(codeline); + compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateMethodRefEntry(codeline)); } if (codeline.hasField()) { - addFieldToConstantPool(codeline); + compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateFieldRefEntry(codeline)); } } - - private void addMethodToConstantPool(CodeLine codeline) { - compiledClass.addConstantPoolEntry(new MethodRefEntry(getOrCreateClassEntry(codeline), createMethodNameAndType(codeline))); - } - - private void addFieldToConstantPool(CodeLine codeline) { - compiledClass.addConstantPoolEntry(new FieldRefEntry(getOrCreateClassEntry(codeline), createFieldNameAndType(codeline))); - } - - private NameAndTypeEntry createMethodNameAndType(CodeLine codeline) { - return new NameAndTypeEntry(new Utf8Entry(codeline.getMethodName()), new Utf8Entry(codeline.getMethodSignature())); - } - - private NameAndTypeEntry createFieldNameAndType(CodeLine codeline) { - return new NameAndTypeEntry(new Utf8Entry(codeline.getField().getName()), new Utf8Entry(TypeMapper.map(codeline.getField().getType()))); - } - - private ClassEntry getOrCreateClassEntry(CodeLine codeline) { - if (codeline.getRef() == Ref.SUPER) { - return compiledClass.addSuperClass(); - - } else if (codeline.getRef() == Ref.THIS) { - return compiledClass.addThisClass(); - } - //TODO other cases - throw new RuntimeException("shouldn't be here"); - } - } diff --git a/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java b/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java new file mode 100644 index 0000000..7339ed3 --- /dev/null +++ b/src/main/java/nl/sander/beejava/ConstantPoolEntryCreator.java @@ -0,0 +1,96 @@ +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.*; + +import java.util.HashMap; +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; + } + + FieldRefEntry getOrCreateFieldRefEntry(CodeLine codeLine) { + return cache(new FieldRefEntry(getOrCreateClassEntry(codeLine), createFieldNameAndType(codeLine))); + } + + MethodRefEntry getOrCreateMethodRefEntry(CodeLine codeline) { + ClassEntry classEntry = getOrCreateClassEntry(codeline); + NameAndTypeEntry nameAndType = getOrCreateMethodNameAndType(codeline); + return cache(new MethodRefEntry(classEntry, nameAndType)); + } + + private NameAndTypeEntry createFieldNameAndType(CodeLine codeline) { + return cache(new NameAndTypeEntry(cache(new Utf8Entry(codeline.getField().getName())), cache(new Utf8Entry(TypeMapper.map(codeline.getField().getType()))))); + } + + private NameAndTypeEntry getOrCreateMethodNameAndType(CodeLine codeline) { + return new NameAndTypeEntry( + cache(new Utf8Entry(codeline.getMethodName())), + cache(new Utf8Entry(codeline.getMethodSignature()))); + } + + 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; + + } else if (codeline.getRef() == Ref.THIS) { + addThisClass(); + return compiledClass.getThisClass(); + } + //TODO other cases + throw new RuntimeException("shouldn't be here"); + } + + void addThisClass(){ + ClassEntry classEntry = getClassEntry(compiledClass.getBeeClass().getName()); + compiledClass.setThisClass(classEntry); + } + + private ClassEntry getClassEntry(String externalClassName) { + return cache(new ClassEntry(cache(new Utf8Entry(internalName(externalClassName))))); + } + + public void addInterfaces() { + compiledClass.getBeeClass().getInterfaces().forEach(interfase -> { + ClassEntry interfaceEntry = cache(new ClassEntry(cache(new Utf8Entry(internalName(interfase.getName()))))); + compiledClass.addInterface(interfaceEntry); + compiledClass.addConstantPoolEntry(interfaceEntry); + }); + } + + + + public void addFields() { + compiledClass.getBeeClass().getFields().forEach(field -> { + // TODO + }); + } + + private 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. + // Can't check for equality unless you create a potential new entry first + int hash = newEntry.hashCode(); + return (T) cache.computeIfAbsent(hash, k -> newEntry); + // 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/constantpool/entry/ClassEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/ClassEntry.java index 4cdea81..36e864d 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/ClassEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/ClassEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class ClassEntry extends ConstantPoolEntry { private static final byte TAG = 7; private final Utf8Entry name; @@ -24,4 +26,17 @@ public class ClassEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG, upperByte(getNameIndex()), lowerByte(getNameIndex())}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ClassEntry that = (ClassEntry) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/DoubleEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/DoubleEntry.java index 75ad5e1..64e0dd6 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/DoubleEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/DoubleEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class DoubleEntry extends LeafConstantPoolEntry { +import java.util.Objects; + +public class DoubleEntry extends LeafEntry { private static final byte TAG = 6; private final double doubleVal; @@ -15,4 +17,16 @@ public class DoubleEntry extends LeafConstantPoolEntry { getByte(bits, 24), getByte(bits, 16), getByte(bits, 8), (byte) (bits & 0xFF)}; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DoubleEntry that = (DoubleEntry) o; + return Double.compare(that.doubleVal, doubleVal) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(doubleVal); + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/DynamicEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/DynamicEntry.java index e65babd..4f39643 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/DynamicEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/DynamicEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class DynamicEntry extends ConstantPoolEntry { private static final byte TAG = 17; private final int bootstrapMethodIndex; // TODO @@ -18,4 +20,18 @@ public class DynamicEntry extends ConstantPoolEntry { public int getNameAndTypeIndex() { return nameAndType.getIndex(); } + +// @Override +// public boolean equals(Object o) { +// if (this == o) return true; +// if (o == null || getClass() != o.getClass()) return false; +// DynamicEntry that = (DynamicEntry) o; +// return bootstrapMethodIndex == that.bootstrapMethodIndex && +// nameAndType.equals(that.nameAndType); +// } +// +// @Override +// public int hashCode() { +// return Objects.hash(bootstrapMethodIndex, nameAndType); +// } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/FieldRefEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/FieldRefEntry.java index c4fd172..5baf09d 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/FieldRefEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/FieldRefEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class FieldRefEntry extends ConstantPoolEntry { private static final byte TAG = 9; @@ -33,4 +35,17 @@ public class FieldRefEntry extends ConstantPoolEntry { return new byte[]{TAG}; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FieldRefEntry that = (FieldRefEntry) o; + return classEntry.equals(that.classEntry) && + nameAndTypeEntry.equals(that.nameAndTypeEntry); + } + + @Override + public int hashCode() { + return Objects.hash(classEntry, nameAndTypeEntry); + } } 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 be5a3ac..6c67304 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/FloatEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/FloatEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class FloatEntry extends LeafConstantPoolEntry { +import java.util.Objects; + +public class FloatEntry extends LeafEntry { private static final byte TAG = 4; private final float floatVal; @@ -16,6 +18,19 @@ public class FloatEntry extends LeafConstantPoolEntry { '}'; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FloatEntry that = (FloatEntry) o; + return Float.compare(that.floatVal, floatVal) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(floatVal); + } + @Override public byte[] getBytes() { long bits = Float.floatToRawIntBits(floatVal); 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 80455a4..586847c 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/IntegerEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/IntegerEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class IntegerEntry extends LeafConstantPoolEntry { +import java.util.Objects; + +public class IntegerEntry extends LeafEntry { private static final byte TAG = 3; private final int intVal; @@ -20,4 +22,17 @@ public class IntegerEntry extends LeafConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG, (byte) (intVal >>> 24), (byte) (intVal >>> 16), (byte) (intVal >>> 8), (byte) (intVal & 0xFF)}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + IntegerEntry that = (IntegerEntry) o; + return intVal == that.intVal; + } + + @Override + public int hashCode() { + return Objects.hash(intVal); + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/InterfaceMethodRefEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/InterfaceMethodRefEntry.java index fdbf54d..438d256 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/InterfaceMethodRefEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/InterfaceMethodRefEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class InterfaceMethodRefEntry extends ConstantPoolEntry { private static final byte TAG = 11; @@ -25,4 +27,18 @@ public class InterfaceMethodRefEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + InterfaceMethodRefEntry that = (InterfaceMethodRefEntry) o; + return classEntry.equals(that.classEntry) && + nameAndTypeEntry.equals(that.nameAndTypeEntry); + } + + @Override + public int hashCode() { + return Objects.hash(classEntry, nameAndTypeEntry); + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/InvokeDynamicEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/InvokeDynamicEntry.java index 8ca4e83..83b1441 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/InvokeDynamicEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/InvokeDynamicEntry.java @@ -24,5 +24,7 @@ public class InvokeDynamicEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG}; } + + // TODO equals and hashcode } //TODO implement later \ No newline at end of file diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/LeafConstantPoolEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/LeafEntry.java similarity index 77% rename from src/main/java/nl/sander/beejava/constantpool/entry/LeafConstantPoolEntry.java rename to src/main/java/nl/sander/beejava/constantpool/entry/LeafEntry.java index 72411f6..c783be6 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/LeafConstantPoolEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/LeafEntry.java @@ -6,7 +6,7 @@ import java.util.Set; /** * Is a constant without children */ -public abstract class LeafConstantPoolEntry extends ConstantPoolEntry { +public abstract class LeafEntry extends ConstantPoolEntry { @Override public Set getChildren() { return Collections.emptySet(); diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/LongEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/LongEntry.java index 83aa546..d9c2cd6 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/LongEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/LongEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class LongEntry extends LeafConstantPoolEntry { +import java.util.Objects; + +public class LongEntry extends LeafEntry { private static final byte TAG = 5; private final long longVal; @@ -22,5 +24,16 @@ public class LongEntry extends LeafConstantPoolEntry { getByte(longVal, 24), getByte(longVal, 16), getByte(longVal, 8), (byte) (longVal & 0xFF)}; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + LongEntry longEntry = (LongEntry) o; + return longVal == longEntry.longVal; + } + @Override + public int hashCode() { + return Objects.hash(longVal); + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/MethodHandleEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/MethodHandleEntry.java index a596a6b..8d71e83 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/MethodHandleEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/MethodHandleEntry.java @@ -30,4 +30,6 @@ public class MethodHandleEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG}; } + + //TODO equals and hashcode } \ No newline at end of file diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/MethodRefEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/MethodRefEntry.java index 91533d5..a5dd0be 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/MethodRefEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/MethodRefEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class MethodRefEntry extends ConstantPoolEntry { private static final byte TAG = 10; @@ -24,4 +26,28 @@ public class MethodRefEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG, upperByte(getClassIndex()), lowerByte(getClassIndex()), upperByte(getNameAndTypeIndex()), lowerByte(getNameAndTypeIndex())}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MethodRefEntry that = (MethodRefEntry) o; + return classRef.equals(that.classRef) && + nameAndType.equals(that.nameAndType); + } + + @Override + public int hashCode() { + return Objects.hash(classRef, nameAndType); + } + + @Override + public String toString() { + return "MethodRefEntry{" + + "classIndex=" + getClassIndex() + + ", nameAndTypeIndex=" + getNameAndTypeIndex() + + '}'; + } + + } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/MethodTypeEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/MethodTypeEntry.java index c658869..390686b 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/MethodTypeEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/MethodTypeEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class MethodTypeEntry extends ConstantPoolEntry { private static final byte TAG = 16; @@ -9,15 +11,27 @@ public class MethodTypeEntry extends ConstantPoolEntry { this.methodDescriptor = methodDescriptor; } + public byte[] getBytes() { + return new byte[]{TAG}; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MethodTypeEntry that = (MethodTypeEntry) o; + return methodDescriptor.equals(that.methodDescriptor); + } + + @Override + public int hashCode() { + return Objects.hash(methodDescriptor); + } + @Override public String toString() { return "MethodTypeEntry{" + "methodDescriptor=" + methodDescriptor + '}'; } - - - public byte[] getBytes() { - return new byte[]{TAG}; - } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/ModuleEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/ModuleEntry.java index db5e4ce..d419d14 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/ModuleEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/ModuleEntry.java @@ -1,5 +1,7 @@ package nl.sander.beejava.constantpool.entry; +import java.util.Objects; + public class ModuleEntry extends ConstantPoolEntry { private static final byte TAG = 19; @@ -18,4 +20,24 @@ public class ModuleEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG, upperByte(getNameIndex()), lowerByte(getNameIndex())}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModuleEntry that = (ModuleEntry) o; + return nameEntry.equals(that.nameEntry); + } + + @Override + public int hashCode() { + return Objects.hash(nameEntry); + } + + @Override + public String toString() { + return "ModuleEntry{" + + "nameEntryIndex" + getNameIndex() + + '}'; + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/NameAndTypeEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/NameAndTypeEntry.java index 31a9976..5842f99 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/NameAndTypeEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/NameAndTypeEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class NameAndTypeEntry extends ConstantPoolEntry { +import java.util.Objects; + +public final class NameAndTypeEntry extends ConstantPoolEntry { private static final byte TAG = 12; private final Utf8Entry name; private final Utf8Entry descriptor; @@ -20,6 +22,24 @@ public class NameAndTypeEntry extends ConstantPoolEntry { return descriptor.getIndex(); } + public byte[] getBytes() { + return new byte[]{TAG, upperByte(getNameIndex()), lowerByte(getNameIndex()), upperByte(getDescriptorIndex()), lowerByte(getDescriptorIndex())}; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + NameAndTypeEntry that = (NameAndTypeEntry) o; + return name.equals(that.name) && + descriptor.equals(that.descriptor); + } + + @Override + public int hashCode() { + return Objects.hash(name, descriptor); + } + @Override public String toString() { return "NameAndTypeEntry{" + @@ -28,7 +48,4 @@ public class NameAndTypeEntry extends ConstantPoolEntry { '}'; } - public byte[] getBytes() { - return new byte[]{TAG, upperByte(getNameIndex()), lowerByte(getNameIndex()), upperByte(getDescriptorIndex()), lowerByte(getDescriptorIndex())}; - } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/PackageEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/PackageEntry.java index 1e74ef0..4a30bf7 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/PackageEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/PackageEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class PackageEntry extends ConstantPoolEntry { +import java.util.Objects; + +public final class PackageEntry extends ConstantPoolEntry { private static final byte TAG = 20; private final Utf8Entry name; @@ -16,4 +18,24 @@ public class PackageEntry extends ConstantPoolEntry { public byte[] getBytes() { return new byte[]{TAG, upperByte(getNameIndex()), lowerByte(getNameIndex())}; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PackageEntry that = (PackageEntry) o; + return name.equals(that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + + @Override + public String toString() { + return "PackageEntry{" + + "nameIndex=" + getNameIndex() + + '}'; + } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/StringEntry.java b/src/main/java/nl/sander/beejava/constantpool/entry/StringEntry.java index f34e2b1..f1f7bad 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/StringEntry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/StringEntry.java @@ -1,6 +1,8 @@ package nl.sander.beejava.constantpool.entry; -public class StringEntry extends ConstantPoolEntry { +import java.util.Objects; + +public final class StringEntry extends ConstantPoolEntry { private static final byte TAG = 8; private final Utf8Entry utf8; @@ -12,14 +14,27 @@ public class StringEntry extends ConstantPoolEntry { return utf8.getIndex(); } + public byte[] getBytes() { + return new byte[]{TAG, upperByte(getUtf8Index()), lowerByte(getUtf8Index())}; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + StringEntry that = (StringEntry) o; + return utf8.equals(that.utf8); + } + + @Override + public int hashCode() { + return Objects.hash(utf8); + } + @Override public String toString() { return "StringEntry{" + "utf8Index=" + getUtf8Index() + '}'; } - - public byte[] getBytes() { - return new byte[]{TAG, upperByte(getUtf8Index()), lowerByte(getUtf8Index())}; - } } diff --git a/src/main/java/nl/sander/beejava/constantpool/entry/Utf8Entry.java b/src/main/java/nl/sander/beejava/constantpool/entry/Utf8Entry.java index f9fb06b..5f0537b 100644 --- a/src/main/java/nl/sander/beejava/constantpool/entry/Utf8Entry.java +++ b/src/main/java/nl/sander/beejava/constantpool/entry/Utf8Entry.java @@ -1,8 +1,9 @@ package nl.sander.beejava.constantpool.entry; import java.nio.charset.StandardCharsets; +import java.util.Objects; -public class Utf8Entry extends LeafConstantPoolEntry { +public final class Utf8Entry extends LeafEntry { private static final byte TAG = 1; private final String value; @@ -14,20 +15,33 @@ public class Utf8Entry extends LeafConstantPoolEntry { return value; } + public byte[] getBytes() { + byte[] utf8Bytes = value.getBytes(StandardCharsets.UTF_8); + byte[] bytes = new byte[utf8Bytes.length + 3]; + bytes[0] = TAG; + bytes[1] = upperByte(utf8Bytes.length); + bytes[2] = lowerByte(utf8Bytes.length); + System.arraycopy(utf8Bytes, 0, bytes, 3, utf8Bytes.length); + return bytes; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Utf8Entry utf8Entry = (Utf8Entry) o; + return value.equals(utf8Entry.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + @Override public String toString() { return "Utf8Entry{" + "value='" + value + '\'' + '}'; } - - public byte[] getBytes() { - byte[] utf8Bytes = value.getBytes(StandardCharsets.UTF_8); - byte[] bytes = new byte[utf8Bytes.length + 3]; - bytes[0] = TAG; - bytes[1] = upperByte(bytes.length); - bytes[2] = lowerByte(bytes.length); - System.arraycopy(utf8Bytes, 0, bytes, 3, utf8Bytes.length); - return bytes; - } } diff --git a/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java b/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java index a7f3018..d8c194b 100644 --- a/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java +++ b/src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java @@ -2,14 +2,23 @@ package nl.sander.beejava; import org.junit.jupiter.api.Test; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; + public class BytecodeGeneratorTests { @Test - public void testEmpty(){ - BytecodeGenerator.compile(TestData.emptyClass()); + public void testEmpty() throws IOException { + byte[] bytecode = BytecodeGenerator.generate(Compiler.compile(TestData.emptyClass())); + File dir = new File("target/nl/sander/beejava/test"); + dir.mkdirs(); + try (FileOutputStream outputStream = new FileOutputStream(new File(dir, "EmptyBean.class"))) { + outputStream.write(bytecode); + } } @Test - public void testInterface(){ - BytecodeGenerator.compile(TestData.emptyClassWithInterface()); + public void testInterface() { + BytecodeGenerator.generate(Compiler.compile(TestData.emptyClassWithInterface())); } } diff --git a/src/test/java/nl/sander/beejava/CompilerTests.java b/src/test/java/nl/sander/beejava/CompilerTests.java index 30d9780..cf4f4eb 100644 --- a/src/test/java/nl/sander/beejava/CompilerTests.java +++ b/src/test/java/nl/sander/beejava/CompilerTests.java @@ -2,16 +2,12 @@ package nl.sander.beejava; import nl.sander.beejava.api.*; import nl.sander.beejava.constantpool.entry.*; -import nl.sander.beejava.flags.FieldAccessFlag; -import nl.sander.beejava.flags.MethodAccessFlag; import org.junit.jupiter.api.Test; import java.util.Iterator; import java.util.Set; import static nl.sander.beejava.api.CodeLine.line; -import static nl.sander.beejava.api.Opcode.*; -import static nl.sander.beejava.flags.ClassAccessFlag.PUBLIC; import static org.junit.jupiter.api.Assertions.assertEquals; public class CompilerTests { @@ -20,7 +16,7 @@ public class CompilerTests { @Test // This is not a maintainable test public void testMethodRefEntryForSuperConstructor() { BeeClass classWithIntField = TestData.emptyClass(); - CompiledClass compiledClass = new Compiler().compile(classWithIntField); + CompiledClass compiledClass = Compiler.compile(classWithIntField); Set constantTree = compiledClass.getConstantTree(); assertEquals(2, constantTree.size()); ConstantPoolEntry superConstructor = constantTree.iterator().next(); diff --git a/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java b/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java new file mode 100644 index 0000000..554872d --- /dev/null +++ b/src/test/java/nl/sander/beejava/testclasses/BeanWithClassReferences.java @@ -0,0 +1,12 @@ +package nl.sander.beejava.testclasses; + +public class BeanWithClassReferences { + + public void print1(){ + System.out.println("1"); + } + + public void print2(){ + System.out.println("2"); + } +}