created CompiledClass and refactored code into it. Added Interfaces to bytecode

This commit is contained in:
Sander Hautvast 2020-11-09 16:04:35 +01:00
parent 3e699d0754
commit 8eda63261b
35 changed files with 440 additions and 319 deletions

View file

@ -0,0 +1,41 @@
public class nl.sander.beejava.testclasses.EmptyBeanWithInterface implements java.io.Serializable
minor version: 0
major version: 58
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #7 // nl/sander/beejava/testclasses/EmptyBeanWithInterface
super_class: #2 // java/lang/Object
interfaces: 1, fields: 0, methods: 1, 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 = Class #8 // nl/sander/beejava/testclasses/EmptyBeanWithInterface
#8 = Utf8 nl/sander/beejava/testclasses/EmptyBeanWithInterface
#9 = Class #10 // java/io/Serializable
#10 = Utf8 java/io/Serializable
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 LocalVariableTable
#14 = Utf8 this
#15 = Utf8 Lnl/sander/beejava/testclasses/EmptyBeanWithInterface;
#16 = Utf8 SourceFile
#17 = Utf8 EmptyBeanWithInterface.java
{
public nl.sander.beejava.testclasses.EmptyBeanWithInterface();
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 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lnl/sander/beejava/testclasses/EmptyBeanWithInterface;
}
SourceFile: "EmptyBeanWithInterface.java"

View file

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

View file

@ -0,0 +1,64 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeClass;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.flags.ClassAccessFlag;
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 ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator();
public BytecodeGenerator(BeeClass beeClass) {
this.beeClass = beeClass;
}
public static byte[] compile(BeeClass beeClass) {
BytecodeGenerator bytecodeGenerator = new BytecodeGenerator(beeClass);
return bytecodeGenerator.doCompile();
}
private byte[] doCompile() {
ByteBuf buf = new ByteBuf();
buf.addU8(0xCA, 0xFE, 0xBA, 0xBE);
buf.addU16(beeClass.getClassFileVersion().getMinor());
buf.addU16(beeClass.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(compiledClass.geThisIndex());
buf.addU16(compiledClass.getSuperIndex());
buf.addU16(compiledClass.getInterfaces().size());
compiledClass.getInterfaces().forEach(interfase -> buf.addU16(interfase.getIndex()));
int x = 1;
for (ConstantPoolEntry e : constantPool) {
System.out.println((x++) + ":" + e);
}
printBytes(buf);
return buf.toBytes();
}
//TODO remove
private void printBytes(ByteBuf buf) {
byte[] bytes = buf.toBytes();
int count = 0;
for (byte b : bytes) {
System.out.print(String.format("%2s", Integer.toHexString(b & 0xFF)).replace(' ', '0') + (count % 2 == 0 ? "" : " "));
if (++count > 15) {
count = 0;
System.out.println();
}
}
}
}

View file

@ -0,0 +1,80 @@
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 {
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) {
this.beeClass = beeClass;
}
public int getSuperIndex() {
return superClass.getIndex();
}
public int geThisIndex() {
return thisClass.getIndex();
}
public ClassEntry superClass() {
if (superClass == null) {
superClass = createClassEntry(beeClass.getSuperClass().getName());
}
return superClass;
}
public ClassEntry thisClass() {
if (thisClass == null) {
thisClass = createClassEntry(internalName(beeClass.getName()));
}
return thisClass;
}
public Set<ConstantPoolEntry> getConstantTree() {
return constantTree;
}
public Set<ClassEntry> getInterfaces() {
return interfaces;
}
public void addInterface(ClassEntry interfaceEntry) {
interfaces.add(interfaceEntry);
}
public void addConstantPoolEntry(ConstantPoolEntry interfaceEntry) {
constantTree.add(interfaceEntry);
}
public void setThisClass() {
constantTree.add(thisClass());
}
public void setInterfaces() {
beeClass.getInterfaces().forEach(interfase -> {
ClassEntry interfaceEntry = new ClassEntry(new Utf8Entry(internalName(interfase.getName())));
addInterface(interfaceEntry);
addConstantPoolEntry(interfaceEntry);
});
}
private ClassEntry createClassEntry(String name) {
return new ClassEntry(new Utf8Entry(internalName(name)));
}
private String internalName(String name) {
return name.replaceAll("\\.", "/");
}
}

View file

@ -1,64 +1,83 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeClass;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.NodeConstant;
import nl.sander.beejava.flags.ClassAccessFlag;
import nl.sander.beejava.util.ByteBuf;
import java.util.Set;
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.
* <p>
* A client must supply a {@link BeeClass} containing a set of {@link CodeLine}s that is assumed to be correct.
* It doesn't check if a valid state is reached.
*/
/* So the name isn't entirely correct. Waiting for inspiration.
* also TODO make sure entries aren't duplicates
*/
public class Compiler {
private CompiledClass compiledClass;
private final BeeClass beeClass; // maybe not a member?
private final ConstantTreeCreator constantTreeCreator = new ConstantTreeCreator();
private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator();
/**
* Creates a Set of nested entries that make up a single reference. For instance a class reference whose name is a utf8 reference.
* In the constant pool they are consecutive entries, but they are created like nodes in a tree structure that models their relation.
*
* @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);
// TODO update constantTree for fields ?
// TODO update constantTree for methods
public Compiler(BeeClass beeClass) {
this.beeClass = beeClass;
compiledClass.setThisClass();
compiledClass.setInterfaces();
return compiledClass;
}
public static byte[] compile(BeeClass beeClass) {
Compiler compiler = new Compiler(beeClass);
return compiler.doCompile();
private void updateConstantPool(ContainsCode codeContainer) {
codeContainer.getCode().forEach(this::updateConstantPool);
}
private byte[] doCompile() {
ByteBuf buf = new ByteBuf();
buf.addU8(0xCA, 0xFE, 0xBA, 0xBE);
buf.addU16(beeClass.getClassFileVersion().getMinor());
buf.addU16(beeClass.getClassFileVersion().getMajor());
Set<NodeConstant> constantTree = constantTreeCreator.createConstantTree(beeClass);
ConstantPool constantPool = constantPoolCreator.createConstantPool(constantTree);
buf.addU16(constantPool.getLength());
buf.addU8(constantPool.getBytes());
buf.addU16(ClassAccessFlag.getSum(beeClass.getAccessFlags()));
buf.addU16(constantTreeCreator.getThisClass().getIndex());
buf.addU16(constantTreeCreator.getSuperClass().getIndex());
int x = 1;
for (NodeConstant e : constantPool) {
System.out.println((x++) + ":" + e);
}
printBytes(buf);
return buf.toBytes();
/*
* scan code line for items that need adding to the constant pool
*/
private void updateConstantPool(CodeLine codeline) {
if (codeline.hasMethod()) {
addMethod(codeline);
}
//TODO remove
private void printBytes(ByteBuf buf) {
byte[] bytes = buf.toBytes();
int count = 0;
for (byte b : bytes) {
System.out.print(String.format("%2s", Integer.toHexString(b & 0xFF)).replace(' ', '0') + (count % 2 == 0 ? "" : " "));
if (++count > 15) {
count = 0;
System.out.println();
}
if (codeline.hasField()) {
addField(codeline);
}
}
private void addMethod(CodeLine codeline) {
compiledClass.addConstantPoolEntry(new MethodRefEntry(getOrCreateClassEntry(codeline), createMethodNameAndType(codeline)));
}
private void addField(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.superClass();
} else if (codeline.getRef() == Ref.THIS) {
return compiledClass.thisClass();
}
//TODO other cases
throw new RuntimeException("shouldn't be here");
}
}

View file

@ -1,7 +1,7 @@
package nl.sander.beejava;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.NodeConstant;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import java.util.Set;
@ -13,33 +13,33 @@ public class ConstantPoolCreator {
private int index; // the current index that will be assigned to a constant pool entry. It needs to be unique for each entry.
// References to other elements in the pool are made through indexes, so they have to be valid to guarantee that the class can be loaded by the JVM.
public ConstantPool createConstantPool(Set<NodeConstant> constantTree) {
public ConstantPool createConstantPool(Set<ConstantPoolEntry> constantTree) {
constantPool = new ConstantPool();
index = 0;
updateToplevelElements(constantTree);
return constantPool;
}
private void updateToplevelElements(Set<NodeConstant> children) {
for (NodeConstant child : children) {
private void updateToplevelElements(Set<ConstantPoolEntry> children) {
for (ConstantPoolEntry child : children) {
addToPool(child);
updateChildElements(child.getChildren());
// first the complete toplevel element including it's children, then next toplevel element
}
}
private void updateChildElements(Set<NodeConstant> children) {
private void updateChildElements(Set<ConstantPoolEntry> children) {
// first all direct children
for (NodeConstant child : children) {
for (ConstantPoolEntry child : children) {
addToPool(child);
}
// then further lineage
for (NodeConstant child : children) {
for (ConstantPoolEntry child : children) {
updateChildElements(child.getChildren());
}
}
private void addToPool(NodeConstant entry) {
private void addToPool(ConstantPoolEntry entry) {
index += 1;
entry.setIndex(index);
constantPool.add(entry);

View file

@ -1,121 +0,0 @@
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.LinkedHashSet;
import java.util.Set;
/**
* Builds a set of a tree of constant pool entries that refer to each other.
* <p>
* A client must supply a {@link BeeClass} containing a set of {@link CodeLine}s that is assumed to be correct.
* It doesn't check if a valid state is reached.
*/
/* So the name isn't entirely correct. Waiting for inspiration.
* also TODO make sure entries aren't duplicates
*/
public class ConstantTreeCreator {
private final Set<NodeConstant> constantTree = new LinkedHashSet<>();
private BeeClass beeClass;
private ClassEntry thisClass;
private ClassEntry superClass;
/**
* Creates a Set of nested entries that make up a single reference. For instance a class reference whose name is a utf8 reference.
* In the constant pool they are consecutive entries, but they are created like nodes in a tree structure that models their relation.
*
* @param beeClass the Class object for which the constant pool needs to be created
* @return a Set of constant pool entries
*/
public Set<NodeConstant> createConstantTree(BeeClass beeClass) {
constantTree.clear();
this.beeClass = beeClass;
beeClass.getConstructors().forEach(this::updateConstantTree);
// TODO update constantTree for fields ?
// TODO update constantTree for methods
constantTree.add(getThisClassRef(beeClass));
return constantTree;
}
/*
* might be null if no methods refer to _this_
*/
private ClassEntry getThisClassRef(BeeClass beeClass) {
if (thisClass == null) {
thisClass = new ClassEntry(new Utf8Entry(internalName(beeClass.getName())));
}
return thisClass;
}
public ClassEntry getThisClass() {
return thisClass;
}
public ClassEntry getSuperClass() {
return superClass;
}
private void updateConstantTree(ContainsCode codeContainer) {
codeContainer.getCode().forEach(this::updateConstantTree);
}
/*
* scan code line for items that need adding to the constant pool
*/
private void updateConstantTree(CodeLine codeline) {
if (codeline.hasMethod()) {
addMethod(codeline);
}
if (codeline.hasField()) {
addField(codeline);
}
}
private void addMethod(CodeLine codeline) {
constantTree.add(new MethodRefEntry(getOrCreateClassEntry(codeline), createMethodNameAndType(codeline)));
}
private void addField(CodeLine codeline) {
constantTree.add(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) {
if (superClass == null) {
superClass = createClassEntry(beeClass.getSuperClass().getName());
}
return superClass;
} else if (codeline.getRef() == Ref.THIS) {
if (thisClass == null) {
thisClass = createClassEntry(beeClass.getName());
}
return thisClass;
}
//TODO other cases
throw new RuntimeException("shouldn't be here");
}
private ClassEntry createClassEntry(String name) {
return new ClassEntry(new Utf8Entry(internalName(name)));
}
private String internalName(String name) {
return name.replaceAll("\\.", "/");
}
}

View file

@ -67,6 +67,10 @@ public class BeeClass {
return superClass;
}
public Set<Class<?>> getInterfaces() {
return interfaces;
}
public Set<BeeField> getFields() {
return fields;
}

View file

@ -1,14 +1,13 @@
package nl.sander.beejava.constantpool;
import nl.sander.beejava.constantpool.entry.NodeConstant;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.util.ByteBuf;
import java.util.ArrayList;
import java.util.List;
public class ConstantPool extends ArrayList<NodeConstant>{
public class ConstantPool extends ArrayList<ConstantPoolEntry>{
public int getIndex(NodeConstant entry) {
public int getIndex(ConstantPoolEntry entry) {
for (int i = 0; i < size(); i++) {
if (get(i) == entry) {
return i + 1;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class ClassEntry extends NodeConstant {
public class ClassEntry extends ConstantPoolEntry {
private static final byte TAG = 7;
private final Utf8Entry name;

View file

@ -4,12 +4,12 @@ import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
public abstract class NodeConstant {
protected final Set<NodeConstant> children; // TODO decide whether or not to keep this. Could also make getChildren abstract and make it return a new list every time
public abstract class ConstantPoolEntry {
protected final Set<ConstantPoolEntry> children; // TODO decide whether or not to keep this. Could also make getChildren abstract and make it return a new list every time
// TODO to save storage. getChildren should only be called once, so it is probably more efficient like that
private int index; // the index of the entry is not known until after creation of the complete tree, so cannot be final, but it should not be updated.
protected NodeConstant(NodeConstant... children) {
protected ConstantPoolEntry(ConstantPoolEntry... children) {
this.children = new LinkedHashSet<>();
this.children.addAll(Arrays.asList(children)); // java8 way destroys order, not desastrous, but I like to preserve it.
}
@ -37,7 +37,7 @@ public abstract class NodeConstant {
this.index = index;
}
public Set<NodeConstant> getChildren() {
public Set<ConstantPoolEntry> getChildren() {
return children;
}

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class DoubleEntry extends LeafConstant {
public class DoubleEntry extends LeafConstantPoolEntry {
private static final byte TAG = 6;
private final double doubleVal;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class DynamicEntry extends NodeConstant {
public class DynamicEntry extends ConstantPoolEntry {
private static final byte TAG = 17;
private final int bootstrapMethodIndex; // TODO
private final NameAndTypeEntry nameAndType;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class FieldRefEntry extends NodeConstant {
public class FieldRefEntry extends ConstantPoolEntry {
private static final byte TAG = 9;
private final ClassEntry classEntry;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class FloatEntry extends LeafConstant {
public class FloatEntry extends LeafConstantPoolEntry {
private static final byte TAG = 4;
private final float floatVal;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class IntegerEntry extends LeafConstant {
public class IntegerEntry extends LeafConstantPoolEntry {
private static final byte TAG = 3;
private final int intVal;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class InterfaceMethodRefEntry extends NodeConstant {
public class InterfaceMethodRefEntry extends ConstantPoolEntry {
private static final byte TAG = 11;
private final ClassEntry classEntry;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class InvokeDynamicEntry extends NodeConstant {
public class InvokeDynamicEntry extends ConstantPoolEntry {
private static final byte TAG = 18;
private final int bootstrapMethodAttrIndex; //??

View file

@ -6,9 +6,9 @@ import java.util.Set;
/**
* Is a constant without children
*/
public abstract class LeafConstant extends NodeConstant {
public abstract class LeafConstantPoolEntry extends ConstantPoolEntry {
@Override
public Set<NodeConstant> getChildren() {
public Set<ConstantPoolEntry> getChildren() {
return Collections.emptySet();
}
}

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class LongEntry extends LeafConstant {
public class LongEntry extends LeafConstantPoolEntry {
private static final byte TAG = 5;
private final long longVal;

View file

@ -1,7 +1,7 @@
package nl.sander.beejava.constantpool.entry;
//TODO implement later
public class MethodHandleEntry extends NodeConstant {
public class MethodHandleEntry extends ConstantPoolEntry {
private static final byte TAG = 15;
private final int referenceKind;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class MethodRefEntry extends NodeConstant {
public class MethodRefEntry extends ConstantPoolEntry {
private static final byte TAG = 10;
private final ClassEntry classRef;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class MethodTypeEntry extends NodeConstant {
public class MethodTypeEntry extends ConstantPoolEntry {
private static final byte TAG = 16;
private final Utf8Entry methodDescriptor;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class ModuleEntry extends NodeConstant {
public class ModuleEntry extends ConstantPoolEntry {
private static final byte TAG = 19;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class NameAndTypeEntry extends NodeConstant {
public class NameAndTypeEntry extends ConstantPoolEntry {
private static final byte TAG = 12;
private final Utf8Entry name;
private final Utf8Entry descriptor;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class PackageEntry extends NodeConstant {
public class PackageEntry extends ConstantPoolEntry {
private static final byte TAG = 20;
private final Utf8Entry name;

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.constantpool.entry;
public class StringEntry extends NodeConstant {
public class StringEntry extends ConstantPoolEntry {
private static final byte TAG = 8;
private final Utf8Entry utf8;

View file

@ -2,7 +2,7 @@ package nl.sander.beejava.constantpool.entry;
import java.nio.charset.StandardCharsets;
public class Utf8Entry extends LeafConstant {
public class Utf8Entry extends LeafConstantPoolEntry {
private static final byte TAG = 1;
private final String value;

View file

@ -0,0 +1,15 @@
package nl.sander.beejava;
import org.junit.jupiter.api.Test;
public class BytecodeGeneratorTests {
@Test
public void testEmpty(){
BytecodeGenerator.compile(TestData.emptyClass());
}
@Test
public void testInterface(){
BytecodeGenerator.compile(TestData.emptyClassWithInterface());
}
}

View file

@ -1,10 +1,99 @@
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 {
@Test
public void test(){
Compiler.compile(TestData.emptyClass());
// creates simplest class possible and checks the tree, that the ConstantTreeCreator emits
@Test // This is not a maintainable test
public void testMethodRefEntryForSuperConstructor() {
BeeClass classWithIntField = TestData.emptyClass();
CompiledClass compiledClass = new Compiler().compile(classWithIntField);
Set<ConstantPoolEntry> constantTree = compiledClass.getConstantTree();
assertEquals(2, constantTree.size());
ConstantPoolEntry superConstructor = constantTree.iterator().next();
assertEquals(MethodRefEntry.class, superConstructor.getClass());
MethodRefEntry methodRefEntry = (MethodRefEntry) superConstructor;
Set<ConstantPoolEntry> methodRefEntryChildren = methodRefEntry.getChildren();
assertEquals(2, methodRefEntryChildren.size());
Iterator<ConstantPoolEntry> firstChildren = methodRefEntryChildren.iterator();
ConstantPoolEntry child1 = firstChildren.next();
assertEquals(ClassEntry.class, child1.getClass());
ClassEntry classEntry = (ClassEntry) child1;
Set<ConstantPoolEntry> classEntryChildren = classEntry.getChildren();
assertEquals(1, classEntryChildren.size());
ConstantPoolEntry child2 = classEntryChildren.iterator().next();
assertEquals(Utf8Entry.class, child2.getClass());
Utf8Entry className = (Utf8Entry) child2;
assertEquals("java/lang/Object", className.getUtf8());
ConstantPoolEntry child3 = firstChildren.next();
assertEquals(NameAndTypeEntry.class, child3.getClass());
NameAndTypeEntry nameAndTypeEntry = (NameAndTypeEntry) child3;
Set<ConstantPoolEntry> nameAndTypeEntryChildren = nameAndTypeEntry.getChildren();
assertEquals(2, nameAndTypeEntryChildren.size());
Iterator<ConstantPoolEntry> nameAndTypeChildrenIterator = nameAndTypeEntryChildren.iterator();
ConstantPoolEntry child4 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child4.getClass());
Utf8Entry name = (Utf8Entry) child4;
assertEquals("<init>", name.getUtf8());
ConstantPoolEntry child5 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child5.getClass());
Utf8Entry type = (Utf8Entry) child5;
assertEquals("()V", type.getUtf8());
}
private BeeClass createClassWithIntField() {
BeeField intField = BeeField.builder()
.withAccessFlags(FieldAccessFlag.PRIVATE)
.withType(int.class)
.withName("intField")
.build();
BeeParameter intValueParameter = BeeParameter.create(int.class, "intValue");
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withFormalParameters(intValueParameter)
.withCode(
line(0, LOAD, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(2, LOAD, Ref.THIS),
line(3, LOAD, intValueParameter),
line(4, PUT, intField),
line(5, RETURN))
.build();
return BeeClass.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("IntBean")
.withSuperClass(Object.class)
.withFields(intField)
.withConstructors(constructor)
.build();
}
}

View file

@ -1,98 +0,0 @@
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 ConstantTreeCreatorTests {
// creates simplest class possible and checks the tree, that the ConstantTreeCreator emits
@Test // This is not a maintainable test
public void testMethodRefEntryForSuperConstructor() {
BeeClass classWithIntField = TestData.emptyClass();
Set<NodeConstant> constantTree = new ConstantTreeCreator().createConstantTree(classWithIntField);
assertEquals(1, constantTree.size());
NodeConstant superConstructor = constantTree.iterator().next();
assertEquals(MethodRefEntry.class, superConstructor.getClass());
MethodRefEntry methodRefEntry = (MethodRefEntry) superConstructor;
Set<NodeConstant> methodRefEntryChildren = methodRefEntry.getChildren();
assertEquals(2, methodRefEntryChildren.size());
Iterator<NodeConstant> firstChildren = methodRefEntryChildren.iterator();
NodeConstant child1 = firstChildren.next();
assertEquals(ClassEntry.class, child1.getClass());
ClassEntry classEntry = (ClassEntry) child1;
Set<NodeConstant> classEntryChildren = classEntry.getChildren();
assertEquals(1, classEntryChildren.size());
NodeConstant child2 = classEntryChildren.iterator().next();
assertEquals(Utf8Entry.class, child2.getClass());
Utf8Entry className = (Utf8Entry) child2;
assertEquals("java/lang/Object", className.getUtf8());
NodeConstant child3 = firstChildren.next();
assertEquals(NameAndTypeEntry.class, child3.getClass());
NameAndTypeEntry nameAndTypeEntry = (NameAndTypeEntry) child3;
Set<NodeConstant> nameAndTypeEntryChildren = nameAndTypeEntry.getChildren();
assertEquals(2, nameAndTypeEntryChildren.size());
Iterator<NodeConstant> nameAndTypeChildrenIterator = nameAndTypeEntryChildren.iterator();
NodeConstant child4 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child4.getClass());
Utf8Entry name = (Utf8Entry) child4;
assertEquals("<init>", name.getUtf8());
NodeConstant child5 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child5.getClass());
Utf8Entry type = (Utf8Entry) child5;
assertEquals("()V", type.getUtf8());
}
private BeeClass createClassWithIntField() {
BeeField intField = BeeField.builder()
.withAccessFlags(FieldAccessFlag.PRIVATE)
.withType(int.class)
.withName("intField")
.build();
BeeParameter intValueParameter = BeeParameter.create(int.class, "intValue");
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withFormalParameters(intValueParameter)
.withCode(
line(0, LOAD, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(2, LOAD, Ref.THIS),
line(3, LOAD, intValueParameter),
line(4, PUT, intField),
line(5, RETURN))
.build();
return BeeClass.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("IntBean")
.withSuperClass(Object.class)
.withFields(intField)
.withConstructors(constructor)
.build();
}
}

View file

@ -6,12 +6,37 @@ import nl.sander.beejava.api.Ref;
import nl.sander.beejava.api.Version;
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.ClassAccessFlag.PUBLIC;
public class TestData {
public static BeeClass emptyClass() {
return BeeClass.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("EmptyBean")
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode
.withConstructors(getBeeConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
}
public static BeeClass emptyClassWithInterface() {
return BeeClass.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(getBeeConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
}
private static BeeConstructor getBeeConstructor() {
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withCode(
@ -19,14 +44,6 @@ public class TestData {
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(5, RETURN))
.build();
return BeeClass.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("EmptyBean")
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode
.withConstructors(constructor) // There's no default constructor in beejava. The user must always add them
.build();
return constructor;
}
}

View file

@ -0,0 +1,5 @@
package nl.sander.beejava.testclasses;
public class EmptyBean {
}

View file

@ -0,0 +1,7 @@
package nl.sander.beejava.testclasses;
import java.io.Serializable;
public class EmptyBeanWithInterface implements Serializable {
}

View file

@ -1,10 +1,10 @@
package nl.sander.beejava.testclasses;
public class IntBean {
// private int intField;
private int intField;
// public IntBean(int intField) {
// this.intField = intField;
// }
public IntBean(int intField) {
this.intField = intField;
}
}