created CompiledClass and refactored code into it. Added Interfaces to bytecode
This commit is contained in:
parent
3e699d0754
commit
8eda63261b
35 changed files with 440 additions and 319 deletions
|
|
@ -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"
|
||||
6
pom.xml
6
pom.xml
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
64
src/main/java/nl/sander/beejava/BytecodeGenerator.java
Normal file
64
src/main/java/nl/sander/beejava/BytecodeGenerator.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
80
src/main/java/nl/sander/beejava/CompiledClass.java
Normal file
80
src/main/java/nl/sander/beejava/CompiledClass.java
Normal 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("\\.", "/");
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
public static byte[] compile(BeeClass beeClass) {
|
||||
Compiler compiler = new Compiler(beeClass);
|
||||
return compiler.doCompile();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
//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();
|
||||
}
|
||||
}
|
||||
return compiledClass;
|
||||
}
|
||||
|
||||
|
||||
private void updateConstantPool(ContainsCode codeContainer) {
|
||||
codeContainer.getCode().forEach(this::updateConstantPool);
|
||||
}
|
||||
|
||||
/*
|
||||
* scan code line for items that need adding to the constant pool
|
||||
*/
|
||||
private void updateConstantPool(CodeLine codeline) {
|
||||
if (codeline.hasMethod()) {
|
||||
addMethod(codeline);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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("\\.", "/");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -67,6 +67,10 @@ public class BeeClass {
|
|||
return superClass;
|
||||
}
|
||||
|
||||
public Set<Class<?>> getInterfaces() {
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
public Set<BeeField> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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; //??
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
15
src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java
Normal file
15
src/test/java/nl/sander/beejava/BytecodeGeneratorTests.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package nl.sander.beejava.testclasses;
|
||||
|
||||
public class EmptyBean {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package nl.sander.beejava.testclasses;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class EmptyBeanWithInterface implements Serializable {
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue