fixed a bug with wrong utf-8 length and added uniqueness checks. Improved the design

This commit is contained in:
Sander Hautvast 2020-11-10 12:49:10 +01:00
parent d17883701d
commit 78cfcd411f
27 changed files with 567 additions and 133 deletions

View file

@ -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."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#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."<init>":()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"

14
pom.xml
View file

@ -7,7 +7,15 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-bytecodeGenerator-plugin</artifactId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>15</source>
<target>15</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>14</source>
<target>14</target>
@ -21,8 +29,8 @@
<packaging>jar</packaging>
<properties>
<maven.bytecodeGenerator.source>15</maven.bytecodeGenerator.source>
<maven.bytecodeGenerator.target>15</maven.bytecodeGenerator.target>
<maven.compiler.source>14</maven.compiler.source>
<maven.compiler.target>14</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View file

@ -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());

View file

@ -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<ConstantPoolEntry> constantTree = new LinkedHashSet<>();
private final Set<ClassEntry> 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<ConstantPoolEntry> getConstantTree() {
Set<ConstantPoolEntry> getConstantTree() {
return constantTree;
}
public Set<ClassEntry> getInterfaces() {
Set<ClassEntry> 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;
}
}
}

View file

@ -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");
}
}

View file

@ -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<Integer, ConstantPoolEntry> 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 extends ConstantPoolEntry> 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.
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
// }
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -24,5 +24,7 @@ public class InvokeDynamicEntry extends ConstantPoolEntry {
public byte[] getBytes() {
return new byte[]{TAG};
}
// TODO equals and hashcode
}
//TODO implement later

View file

@ -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<ConstantPoolEntry> getChildren() {
return Collections.emptySet();

View file

@ -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);
}
}

View file

@ -30,4 +30,6 @@ public class MethodHandleEntry extends ConstantPoolEntry {
public byte[] getBytes() {
return new byte[]{TAG};
}
//TODO equals and hashcode
}

View file

@ -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() +
'}';
}
}

View file

@ -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};
}
}

View file

@ -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() +
'}';
}
}

View file

@ -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())};
}
}

View file

@ -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() +
'}';
}
}

View file

@ -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())};
}
}

View file

@ -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;
}
}

View file

@ -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()));
}
}

View file

@ -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<ConstantPoolEntry> constantTree = compiledClass.getConstantTree();
assertEquals(2, constantTree.size());
ConstantPoolEntry superConstructor = constantTree.iterator().next();

View file

@ -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");
}
}