first commit i a long while. Should have committed earlier, but tests were failing.

* updated bejava lang
* bug fixes
This commit is contained in:
Sander Hautvast 2021-01-18 20:42:24 +01:00
parent 5e7267c170
commit 22a6930973
54 changed files with 966 additions and 1506 deletions

View file

@ -1,9 +1,9 @@
# beejava # bejava
Beejava is a code creation library, somewhat comparable to javassist or bytebuddy. Bejava (backend java compiler) is a code creation library, somewhat comparable to javassist or bytebuddy.
* It let's you compile java 'opcode' to bytecode. * It let's you compile bejava-lang' to bytecode.
* It does not inspect or enhance existing bytecode, though it could be part of such functionality. * It does not inspect or enhance existing bytecode, though it could be part of such functionality.
What is 'opcode'? What is 'bejava lang'?
The goal of the project is to let developers dynamically create classes, using a simplified version of standard java opcodes. For instance: The goal of the project is to let developers dynamically create classes, using a simplified version of standard java opcodes. For instance:
instead of having to choose between: instead of having to choose between:

View file

@ -2,21 +2,21 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<name>beejava</name> <name>bejava</name>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration> <configuration>
<source>15</source> <release>15</release>
<target>15</target>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<groupId>nl.sander</groupId> <groupId>nl.sander</groupId>
<artifactId>beejava</artifactId> <artifactId>bejava</artifactId>
<version>0.1-SNAPSHOT</version> <version>0.1-SNAPSHOT</version>
<packaging>jar</packaging> <packaging>jar</packaging>

View file

@ -1,14 +1,11 @@
package nl.sander.beejava; package nl.sander.beejava;
import nl.sander.beejava.api.BeeMethod; import nl.sander.beejava.api.*;
import nl.sander.beejava.api.BeeSource; import nl.sander.beejava.classinfo.FieldInfo;
import nl.sander.beejava.api.CodeContainer;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.classinfo.MethodInfo; import nl.sander.beejava.classinfo.MethodInfo;
import nl.sander.beejava.constantpool.ConstantPool; import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry; import nl.sander.beejava.constantpool.entry.ClassEntry;
import nl.sander.beejava.constantpool.entry.FieldRefEntry; import nl.sander.beejava.constantpool.entry.FieldRefEntry;
import nl.sander.beejava.constantpool.entry.MethodRefEntry;
import nl.sander.beejava.constantpool.entry.Utf8Entry; import nl.sander.beejava.constantpool.entry.Utf8Entry;
/** /**
@ -22,7 +19,6 @@ import nl.sander.beejava.constantpool.entry.Utf8Entry;
*/ */
public class Compiler { public class Compiler {
private final CompiledClass compiledClass; private final CompiledClass compiledClass;
private final ConstantPoolEntryCreator constantPoolEntryCreator;
private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator(); private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator();
private Utf8Entry codeAttributeNameEntry; private Utf8Entry codeAttributeNameEntry;
@ -34,7 +30,6 @@ public class Compiler {
*/ */
public Compiler(CompiledClass compiledClass) { public Compiler(CompiledClass compiledClass) {
this.compiledClass = compiledClass; this.compiledClass = compiledClass;
this.constantPoolEntryCreator = new ConstantPoolEntryCreator(compiledClass);
} }
/** /**
@ -51,26 +46,57 @@ public class Compiler {
* construct a CompiledClass object that contains all information for generating the bytecode * construct a CompiledClass object that contains all information for generating the bytecode
*/ */
public CompiledClass compile() { public CompiledClass compile() {
compiledClass.setConstantPool(createConstantPool()); compiledClass.getSource().getFields().forEach(this::addToConstantPool);
compiledClass.getSource().getConstructors().forEach(this::addToConstantPool);
compiledClass.getSource().getMethods().forEach(this::addToConstantPool); // compiledClass.getSource() ?
addClassAndSuperClassEntriesToCompiledClass();
ConstantPool constantPool = constantPoolCreator.createConstantPool(compiledClass.getConstantTree());
compiledClass.setConstantPool(constantPool);
// add refs to super interfaces to class descriptor and constantpool
compiledClass.getSource().getInterfaces().forEach(interfase -> {
ClassEntry interfaceEntry = ConstantPoolEntryCreator.getOrCreateClassEntry(interfase);
compiledClass.addInterface(interfaceEntry);
compiledClass.addConstantPoolEntry(interfaceEntry);
});
// add refs to fields to class descriptor
compiledClass.getSource().getFields().stream()
.map(field ->
new FieldInfo(ConstantPoolEntryCreator.getOrCreateUtf8(field.getName()),
ConstantPoolEntryCreator.getOrCreateUtf8(internalName(field.getType().getName())))
.addAccessFlags(field.getAccessFlags())
)
.forEach(compiledClass::addField);
constantPoolEntryCreator.addInterfaces();
constantPoolEntryCreator.addFields();
addConstructors(); addConstructors();
addMethods(); addMethods();
return compiledClass; return compiledClass;
} }
private ConstantPool createConstantPool() { // why not put this everywhere, it's not like it's ever going to change
compiledClass.getSource().getConstructors().forEach(this::updateConstantPool); private String internalName(String name) {
compiledClass.getSource().getMethods().forEach(this::updateConstantPool); // compiledClass.getSource() ? return name.replaceAll("\\.", "/");
}
compiledClass.setThisClass(constantPoolEntryCreator.addThisClass());
this.codeAttributeNameEntry = constantPoolEntryCreator.getOrCreateUtf8("Code");
private void addClassAndSuperClassEntriesToCompiledClass() {
ClassEntry classEntry = ConstantPoolEntryCreator.getOrCreateClassEntry(compiledClass.getSource().getName());
compiledClass.setThisClass(classEntry);
compiledClass.addConstantPoolEntry(classEntry);
ClassEntry superClass = ConstantPoolEntryCreator.getOrCreateClassEntry(compiledClass.getSource().getSuperClass());
compiledClass.addConstantPoolEntry(superClass);
compiledClass.setSuperClass(superClass);
this.codeAttributeNameEntry = ConstantPoolEntryCreator.getOrCreateUtf8("Code");
compiledClass.addConstantPoolEntry(codeAttributeNameEntry); compiledClass.addConstantPoolEntry(codeAttributeNameEntry);
return constantPoolCreator.createConstantPool(compiledClass.getConstantTree());
} }
public void addConstructors() { public void addConstructors() {
@ -79,8 +105,7 @@ public class Compiler {
.forEach(compiledClass::addMethod); .forEach(compiledClass::addMethod);
} }
/**
/*
* maps methods from the source to a MethodInfo and adds that to the CompiledClass. * maps methods from the source to a MethodInfo and adds that to the CompiledClass.
*/ */
public void addMethods() { public void addMethods() {
@ -88,34 +113,34 @@ public class Compiler {
for (BeeMethod method : compiledClass.getSource().getMethods()) { for (BeeMethod method : compiledClass.getSource().getMethods()) {
compiledClass.addMethod(createMethod(method)); compiledClass.addMethod(createMethod(method));
} }
// compiledClass.getSource().getMethods().stream()
// .map(this::createMethod)
// .forEach(compiledClass::addMethod);
} }
/* /**
* create bytecode for method
* create methodInfo object for classfile * create methodInfo object for classfile
*/ */
private MethodInfo createMethod(CodeContainer method) { private MethodInfo createMethod(CodeContainer method) {
return new MethodInfo( return new MethodInfo(
constantPoolEntryCreator.getOrCreateUtf8(method.getName()), ConstantPoolEntryCreator.getOrCreateUtf8(method.getName()),
constantPoolEntryCreator.getOrCreateUtf8(method.getSignature())) ConstantPoolEntryCreator.getOrCreateUtf8(method.getSignature()))
.addAccessFlags(method.getAccessFlags()) .addAccessFlags(method.getAccessFlags())
.addAttribute(MethodCodeCreator.createCodeAttribute(codeAttributeNameEntry, method)); .addAttribute(MethodCodeAttributeCreator.createCodeAttribute(codeAttributeNameEntry, method));
} }
/* private void addToConstantPool(BeeField field) {
FieldRefEntry fieldRefEntry = ConstantPoolEntryCreator.getOrCreateFieldRefEntry(compiledClass.getSource().getName(), field.getName(), field.getType());
compiledClass.addConstantPoolEntry(fieldRefEntry);
}
/**
* inspect a method or constructor, extract items that need to be added, and add them to the constant pool * inspect a method or constructor, extract items that need to be added, and add them to the constant pool
*/ */
private void updateConstantPool(CodeContainer codeContainer) { private void addToConstantPool(CodeContainer codeContainer) {
compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateUtf8(codeContainer.getName())); compiledClass.addConstantPoolEntry(ConstantPoolEntryCreator.getOrCreateUtf8(codeContainer.getName()));
compiledClass.addConstantPoolEntry(constantPoolEntryCreator.getOrCreateUtf8(codeContainer.getSignature())); compiledClass.addConstantPoolEntry(ConstantPoolEntryCreator.getOrCreateUtf8(codeContainer.getSignature()));
codeContainer.getCode().forEach(this::updateConstantPool); codeContainer.getExpandedCode().forEach(this::updateConstantPool);
} }
/* /**
* 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: * Constantpool uniqueness is maintained in two ways:
@ -124,23 +149,17 @@ public class Compiler {
* 2. the root node (also in aforementioned cache) is not symmetric because it is the only one that can be added to The compiledClass. * 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. * So Compiled class also contains a set of unique root nodes.
*/ */
private void updateConstantPool(CodeLine codeline) { private void updateConstantPool(JavaInstruction instruction) {
if (codeline.hasMethodCall()) { if (instruction.hasMethodRef()) {
MethodRefEntry methodRefEntry = constantPoolEntryCreator.getOrCreateMethodRefEntry(codeline); compiledClass.addConstantPoolEntry(instruction.getMethodRef());
codeline.setAssignedEntry(methodRefEntry);
compiledClass.addConstantPoolEntry(methodRefEntry);
} }
if (codeline.hasRefToOwnField() || codeline.hasRefToExternalField()) { if (instruction.hasFieldRef()) {
FieldRefEntry fieldRefEntry = constantPoolEntryCreator.getOrCreateFieldRefEntry(codeline); compiledClass.addConstantPoolEntry(instruction.getFieldRef());
codeline.setAssignedEntry(fieldRefEntry);
compiledClass.addConstantPoolEntry(fieldRefEntry);
} }
if (codeline.hasConstValue()) { if (instruction.hasConstantRef()) {
ConstantPoolEntry primitiveEntry = constantPoolEntryCreator.getOrCreatePrimitiveEntry(codeline); compiledClass.addConstantPoolEntry(instruction.getConstantEntry());
codeline.setAssignedEntry(primitiveEntry);
compiledClass.addConstantPoolEntry(primitiveEntry);
} }
} }
} }

View file

@ -1,10 +1,7 @@
package nl.sander.beejava; package nl.sander.beejava;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.api.Ref;
import nl.sander.beejava.classinfo.FieldInfo;
import nl.sander.beejava.classinfo.attributes.CodeAttribute;
import nl.sander.beejava.constantpool.entry.*; import nl.sander.beejava.constantpool.entry.*;
import nl.sander.beejava.operands.ConstantOperand;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -14,147 +11,84 @@ import java.util.Map;
* Responsible for creating unique constant pool entries * Responsible for creating unique constant pool entries
*/ */
class ConstantPoolEntryCreator { class ConstantPoolEntryCreator {
private final Map<Integer, ConstantPoolEntry> cache = new HashMap<>();
private final CompiledClass compiledClass;
public ConstantPoolEntryCreator(CompiledClass compiledClass) { private static final Map<Integer, ConstantPoolEntry> cache = new HashMap<>();
this.compiledClass = compiledClass;
private ConstantPoolEntryCreator() {
} }
/* /*
* creates a FieldRefEntry when not found in cache, otherwise gets it from there * creates a FieldRefEntry when not found in cache, otherwise gets it from there
*/ */
FieldRefEntry getOrCreateFieldRefEntry(CodeLine codeLine) { static FieldRefEntry getOrCreateFieldRefEntry(String declaringClassName, String fieldName, Class<?> fieldType) {
return cache(new FieldRefEntry(getOrCreateClassEntry(codeLine), getOrCreateFieldNameAndType(codeLine))); return cache(new FieldRefEntry(getOrCreateClassEntry(declaringClassName), getOrCreateFieldNameAndType(fieldName, fieldType)));
} }
/* /*
* creates a MethodRefEntry when not found in cache, otherwise gets it from there * creates a MethodRefEntry when not found in cache, otherwise gets it from there
*/ */
MethodRefEntry getOrCreateMethodRefEntry(CodeLine codeline) { static MethodRefEntry getOrCreateMethodRefEntry(String className, String methodName, String signature) {
ClassEntry classEntry = getOrCreateClassEntry(codeline); ClassEntry classEntry = getOrCreateClassEntry(className);
return cache(new MethodRefEntry(classEntry, getOrCreateMethodNameAndType(codeline))); return cache(new MethodRefEntry(classEntry, getOrCreateMethodNameAndType(methodName, signature)));
} }
/* /*
* creates a NamAndTypeEntry for a field when not found in cache, otherwise gets it from there * creates a NamAndTypeEntry for a field when not found in cache, otherwise gets it from there
*/ */
private NameAndTypeEntry getOrCreateFieldNameAndType(CodeLine codeline) { private static NameAndTypeEntry getOrCreateFieldNameAndType(String name, Class<?> type) {
if (codeline.hasRefToOwnField()) {
return cache(new NameAndTypeEntry( return cache(new NameAndTypeEntry(
cache(new Utf8Entry(codeline.getOwnfield().getName())), cache(new Utf8Entry(name)),
cache(new Utf8Entry(TypeMapper.map(codeline.getOwnfield().getType()))))); // is actually a shortcut cache(new Utf8Entry(TypeMapper.map(type)))));
} else {//TODO this method may need some work
return cache(new NameAndTypeEntry(
cache(new Utf8Entry(codeline.getExternalfield().getName())),
cache(new Utf8Entry(TypeMapper.map(codeline.getExternalfield().getType())))
));
}
} }
/* /*
* creates a NamAndTypeEntry for a method when not found in cache, otherwise gets it from there * creates a NamAndTypeEntry for a method when not found in cache, otherwise gets it from there
*/ */
private NameAndTypeEntry getOrCreateMethodNameAndType(CodeLine codeline) { private static NameAndTypeEntry getOrCreateMethodNameAndType(String methodName, String methodSignature) {
return new NameAndTypeEntry( return new NameAndTypeEntry(
cache(new Utf8Entry(codeline.getMethodName())), cache(new Utf8Entry(methodName)),
cache(new Utf8Entry(codeline.getMethodSignature()))); cache(new Utf8Entry(methodSignature)));
}
/*
* creates a ClassEntry when not found in cache, otherwise gets it from there
*/
private ClassEntry getOrCreateClassEntry(CodeLine codeline) {
if (codeline.hasRef()) {
if (codeline.getRef() == Ref.SUPER) { // this and super are rather special
ClassEntry superClass = getClassEntry(compiledClass.getSource().getSuperClass().getName());
compiledClass.setSuperClass(superClass);
return superClass;
} else if (codeline.getRef() == Ref.THIS) {
return addThisClass();
}
} else if (codeline.hasClassName()) {
return getClassEntry(codeline.getClassName());
}
throw new RuntimeException("shouldn't be here");
}
/*
* this method gives me a headache. It adds, but also creates a classEntry for the this class
*/
ClassEntry addThisClass() {
ClassEntry classEntry = getClassEntry(compiledClass.getSource().getName());
compiledClass.addConstantPoolEntry(classEntry);
return classEntry;
} }
/* /*
* get or create a ClassEntry * get or create a ClassEntry
*/ */
private ClassEntry getClassEntry(String externalClassName) { public static ClassEntry getOrCreateClassEntry(String externalClassName) {
return cache(new ClassEntry(cache(new Utf8Entry(internalName(externalClassName))))); return cache(new ClassEntry(cache(new Utf8Entry(internalName(externalClassName)))));
} }
/* public static ClassEntry getOrCreateClassEntry(Class<?> type) {
* Adds interfaces to the constant pool as well as the class. return getOrCreateClassEntry(type.getName());
*
* interfaces[] in the class file is an array of cp entries
*/
public void addInterfaces() {
compiledClass.getSource().getInterfaces().forEach(interfase -> {
ClassEntry interfaceEntry = cache(new ClassEntry(cache(new Utf8Entry(internalName(interfase.getName())))));
compiledClass.addInterface(interfaceEntry);
compiledClass.addConstantPoolEntry(interfaceEntry);
});
} }
/* /*
* If a constant is in the codeline, it needs to be added to the constant pool. * If a constant is in the codeline, it needs to be added to the constant pool.
*/ */
public ConstantPoolEntry getOrCreatePrimitiveEntry(CodeLine codeline) { public static ConstantPoolEntry getOrCreatePrimitiveConstantEntry(ConstantOperand operand) {
Object v = codeline.getConstValue(); return cache(switch (operand.getType()) {
case STRING -> new StringEntry(cache(new Utf8Entry(operand.getValue())));
if (v instanceof String) { case INT, BYTE, SHORT -> new IntegerEntry(operand.getValue());
return cache(new StringEntry(cache(new Utf8Entry((String) v)))); case LONG -> new LongEntry(Long.parseLong(operand.getValue()));
} else if (v instanceof Integer) { case FLOAT -> new FloatEntry(operand.getValue());
return cache(new IntegerEntry((Integer) v)); case DOUBLE -> new DoubleEntry(Double.parseDouble(operand.getValue()));
} else if (v instanceof Long) { case CHAR -> new IntegerEntry(Character.codePointAt(operand.getValue(), 0));
return cache(new LongEntry((Long) v)); case BOOLEAN -> new IntegerEntry(Boolean.parseBoolean(operand.getValue()) ? 1 : 0);
} else if (v instanceof Float) { });
return cache(new FloatEntry((Float) v));
} else if (v instanceof Double) {
return cache(new DoubleEntry((Double) v));
}
throw new IllegalStateException("shouldn't be here"); // TODO find out why are you here
} }
/* public static Utf8Entry getOrCreateUtf8(String utf8) {
* maps a field from the source to a FieldInfo and adds that to the CompiledClass.
*/
public void addFields() {
compiledClass.getSource().getFields().stream()
.map(field -> new FieldInfo(
cache(new Utf8Entry(field.getName())),
cache(new Utf8Entry(internalName(field.getType().getName())))
).addAccessFlags(field.getAccessFlags())
)
.forEach(compiledClass::addField);
}
public Utf8Entry getOrCreateUtf8(String utf8) {
return cache(new Utf8Entry(utf8)); return cache(new Utf8Entry(utf8));
} }
// why not put this everywhere, it's not like it's ever going to change // why not put this everywhere, it's not like it's ever going to change
private String internalName(String name) { private static String internalName(String name) {
return name.replaceAll("\\.", "/"); return name.replaceAll("\\.", "/");
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
<T extends ConstantPoolEntry> T cache(T newEntry) { static <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. // if the argument newEntry is found in cache, return the cached entry and discard the argument
// Otherwise store it.
// Can't check for equality unless you create a potential new entry first // Can't check for equality unless you create a potential new entry first
int hash = newEntry.hashCode(); int hash = newEntry.hashCode();
return (T) cache.computeIfAbsent(hash, k -> newEntry); return (T) cache.computeIfAbsent(hash, k -> newEntry);

View file

@ -0,0 +1,51 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeField;
import nl.sander.beejava.flags.AccessFlags;
import java.lang.reflect.Field;
/**
* Union type for an existing standard java field (already loaded) or a field for the class that is being compiled
*/
public class FieldWrapper {
private final Field reflectField;
private final BeeField beefield;
public FieldWrapper(Field reflectField, BeeField beefield) {
this.reflectField = reflectField;
this.beefield = beefield;
}
public Class<?> getType() {
if (reflectField != null) {
return reflectField.getType();
} else {
return beefield.getType();
}
}
public String getName() {
if (reflectField != null) {
return reflectField.getName();
} else {
return beefield.getName();
}
}
public int getModifiers() {
if (reflectField != null) {
return reflectField.getModifiers();
} else {
return AccessFlags.combine(beefield.getAccessFlags());
}
}
public String getOwner() {
if (reflectField != null) {
return reflectField.getDeclaringClass().getName();
} else {
return beefield.getDeclaringClass();
}
}
}

View file

@ -0,0 +1,81 @@
package nl.sander.beejava;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.constantpool.entry.FieldRefEntry;
import nl.sander.beejava.constantpool.entry.MethodRefEntry;
public class JavaInstruction {
private final JavaOpcode opcode;
private final MethodRefEntry methodRefEntry;
private final FieldRefEntry fieldRefEntry;
private final ConstantPoolEntry constantEntry;
private Integer localVariableIndex;
public JavaInstruction(JavaOpcode opcode) {
this(opcode, null, null, null, null);
}
public JavaInstruction(JavaOpcode opcode, int localVariableIndex) {
this(opcode, localVariableIndex, null, null, null);
}
public JavaInstruction(JavaOpcode opcode, ConstantPoolEntry constantEntry) {
this(opcode, null, null, null, constantEntry);
}
public JavaInstruction(JavaOpcode opcode, MethodRefEntry methodRefEntry) {
this(opcode, null, methodRefEntry, null, null);
}
public JavaInstruction(JavaOpcode opcode, FieldRefEntry fieldRefEntry) {
this(opcode, null, null, fieldRefEntry, null);
}
private JavaInstruction(JavaOpcode opcode, Integer localVariableIndex, MethodRefEntry methodRefEntry, FieldRefEntry fieldRefEntry, ConstantPoolEntry constantEntry) {
this.opcode = opcode;
this.localVariableIndex = localVariableIndex;
this.methodRefEntry = methodRefEntry;
this.fieldRefEntry = fieldRefEntry;
this.constantEntry = constantEntry;
}
public JavaOpcode getOpcode() {
return opcode;
}
public MethodRefEntry getMethodRef() {
return methodRefEntry;
}
public FieldRefEntry getFieldRef() {
return fieldRefEntry;
}
public boolean hasMethodRef() {
return methodRefEntry != null;
}
public boolean hasFieldRef() {
return fieldRefEntry != null;
}
public ConstantPoolEntry getEntry() {
if (methodRefEntry != null) {
return methodRefEntry;
} else if (fieldRefEntry != null) {
return fieldRefEntry;
} else if (constantEntry!=null){
return constantEntry;
} else {
return null; // Not sure yet what to do here.
}
}
public boolean hasConstantRef() {
return constantEntry !=null;
}
public ConstantPoolEntry getConstantEntry() {
return constantEntry;
}
}

View file

@ -5,8 +5,20 @@ public enum JavaOpcode {
LDC_W(0x13,true, +1), LDC_W(0x13,true, +1),
LDC2_W ( 0x14, true, +2), LDC2_W ( 0x14, true, +2),
ALOAD ( 0x19, false, +1),
ALOAD_0 ( 0x2a, false, +1), ALOAD_0 ( 0x2a, false, +1),
ALOAD_1 ( 0x2b, false, +1),
ALOAD_2 ( 0x2c, false, +1),
ALOAD_3 ( 0x2d, false, +1),
IRETURN ( 0xac,false, -1),
LRETURN(0xad, false, -2),
FRETURN(0xae,false,-1),
DRETURN(0xaf,false,-2),
ARETURN ( 0xb0,false, -1),
RETURN ( 0xb1,false, 0), RETURN ( 0xb1,false, 0),
GETSTATIC ( 0xb2,true, +1), GETSTATIC ( 0xb2,true, +1),
GETFIELD ( 0xb4,true, +1), GETFIELD ( 0xb4,true, +1),

View file

@ -8,7 +8,7 @@ import nl.sander.beejava.constantpool.entry.MethodRefEntry;
import nl.sander.beejava.constantpool.entry.Utf8Entry; import nl.sander.beejava.constantpool.entry.Utf8Entry;
import nl.sander.beejava.util.ByteBuf; import nl.sander.beejava.util.ByteBuf;
public class MethodCodeCreator { public class MethodCodeAttributeCreator {
public static CodeAttribute createCodeAttribute(Utf8Entry codeAttributeNameEntry, CodeContainer codeContainer) { public static CodeAttribute createCodeAttribute(Utf8Entry codeAttributeNameEntry, CodeContainer codeContainer) {
CodeAttribute codeAttribute = new CodeAttribute(codeAttributeNameEntry); CodeAttribute codeAttribute = new CodeAttribute(codeAttributeNameEntry);
@ -16,10 +16,10 @@ public class MethodCodeCreator {
codeAttribute.setMaxLocals(codeContainer.getFormalParameters().size() + 1); codeAttribute.setMaxLocals(codeContainer.getFormalParameters().size() + 1);
ByteBuf byteBuf = new ByteBuf(); ByteBuf byteBuf = new ByteBuf();
codeContainer.getCode().forEach(codeLine -> { codeContainer.getExpandedCode().forEach(instruction -> {
JavaOpcode javaOpcode = codeLine.getJavaOpcode(); // the opcode we determined in calculateMaxStack JavaOpcode javaOpcode = instruction.getOpcode();
byteBuf.addU8(javaOpcode.getByteCode()); byteBuf.addU8(javaOpcode.getByteCode());
ConstantPoolEntry constantPoolEntry = codeLine.getAssignedEntry(); ConstantPoolEntry constantPoolEntry = instruction.getEntry();
if (constantPoolEntry != null) { if (constantPoolEntry != null) {
if (javaOpcode.isWide()) { if (javaOpcode.isWide()) {
byteBuf.addU16(constantPoolEntry.getIndex()); byteBuf.addU16(constantPoolEntry.getIndex());
@ -37,11 +37,9 @@ public class MethodCodeCreator {
private static int calculateMaxStack(CodeContainer codeContainer) { private static int calculateMaxStack(CodeContainer codeContainer) {
int stackSize = 0; int stackSize = 0;
int maxStackSize = 0; int maxStackSize = 0;
for (CodeLine codeLine : codeContainer.getCode()) { for (JavaInstruction instruction : codeContainer.getExpandedCode()) {
JavaOpcode javaOpcode = OpcodeMapper.mapOpcode(codeLine); stackSize += instruction.getOpcode().getStackDif();
codeLine.setJavaOpcode(javaOpcode); //not really nice that we mutate codeLine, but this way we don't have to calculate the JavaOpcode twice ConstantPoolEntry assignedEntry = instruction.getEntry();
stackSize += javaOpcode.getStackDif();
ConstantPoolEntry assignedEntry = codeLine.getAssignedEntry();
if (assignedEntry instanceof MethodRefEntry) { if (assignedEntry instanceof MethodRefEntry) {
MethodRefEntry methodRefEntry = (MethodRefEntry) assignedEntry; MethodRefEntry methodRefEntry = (MethodRefEntry) assignedEntry;
String argumentTypes = methodRefEntry.getNameAndType().getType(); String argumentTypes = methodRefEntry.getNameAndType().getType();

View file

@ -1,55 +0,0 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.api.Opcode;
import nl.sander.beejava.flags.ClassAccessFlags;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static nl.sander.beejava.JavaOpcode.*;
public class OpcodeMapper {
public static JavaOpcode mapOpcode(CodeLine codeLine) {
Opcode opcode = codeLine.getOpcode();
return switch (opcode) {
case GET -> isStatic(codeLine.getExternalfield()) ? GETSTATIC : GETFIELD;
case LD_VAR -> ALOAD_0;
case LD_CONST -> loadConst(codeLine);
case INVOKE -> invoke(codeLine);
case RETURN -> JavaOpcode.RETURN; //TODO not complete yet
case PUT -> JavaOpcode.PUTFIELD;
default -> throw new IllegalStateException("something not implemented");
};
}
/* TODO cover more cases */
private static JavaOpcode invoke(CodeLine codeLine) {
BeeSource source = codeLine.getOwner().getOwner();
if (source.getAccessFlags().contains(ClassAccessFlags.SUPER) && codeLine.getOwner().isConstructor()) {
return INVOKESPECIAL;
} else {
return INVOKEVIRTUAL;
}
}
private static JavaOpcode loadConst(CodeLine codeLine) {
Object constValue = codeLine.getConstValue();
int index = codeLine.getAssignedEntry().getIndex();
if (constValue instanceof Double || constValue instanceof Long) {
return LDC2_W;
} else {
if (index > 0xff) {
return LDC_W;
} else {
return LDC;
}
}
}
private static boolean isStatic(Field field) {
return (field.getModifiers() & Modifier.STATIC) == Modifier.STATIC;
}
}

View file

@ -0,0 +1,214 @@
package nl.sander.beejava;
import nl.sander.beejava.api.*;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.operands.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;
enum Primitives {
INT, SHORT, BYTE, LONG, FLOAT, DOUBLE, CHAR, BOOLEAN;
public static Primitives from(String value) {
return Arrays.stream(values()).filter(e -> e.toString().equals(value)).findFirst().orElse(null);
}
}
/**
* Translates from BeeJava opcodes to actual java opcodes.
*/
public class OpcodeTranslator {
private final static OpcodeTranslator instance = new OpcodeTranslator();
private final Parser parser = new Parser();
private BeeSource beeSource;
/*
* trying to make SourceExpander reusable.
* @not_multithreaded.
* //TODO should not mutate beesource
*/
public static void translate(BeeSource beeSource) {
instance.init(beeSource);
instance.doExpand();
}
private void init(BeeSource beeSource) {
this.beeSource = beeSource;
}
private void doExpand() {
beeSource.getConstructors().forEach(this::translate);
beeSource.getMethods().forEach(this::translate);
}
private void translate(CodeContainer codeContainer) {
var methodInstructions = codeContainer.getCode().stream()
.flatMap(o -> translate(codeContainer, o).stream())
.collect(Collectors.toList());
codeContainer.setExpandedCode(methodInstructions);
}
private List<JavaInstruction> translate(CodeContainer codeContainer, CodeLine codeLine) {
var operand = parser.parse(codeLine);
return eval(codeContainer, codeLine.getOpcode(), operand);
}
private List<JavaInstruction> eval(CodeContainer codeContainer, Opcode opcode, Operand operand) {
final List<JavaInstruction> instructions = new ArrayList<>();
if (operand instanceof MethodOperand) {
var mo = (MethodOperand) operand;
if ("this".equals(mo.className)) {
instructions.add(new JavaInstruction(JavaOpcode.ALOAD_0));
}
}
if (opcode == Opcode.LOAD) {
if (operand instanceof ConstantOperand) {
ConstantPoolEntry constantEntry = ConstantPoolEntryCreator.getOrCreatePrimitiveConstantEntry((ConstantOperand) operand);
instructions.add(new JavaInstruction(JavaOpcode.LDC, constantEntry));
}
} else if (opcode == Opcode.RETURN && operand instanceof VoidOperand) {
instructions.add(new JavaInstruction(JavaOpcode.RETURN));
} else if (opcode == Opcode.GET) {
assert operand instanceof FieldOperand;
var fieldOperand = (FieldOperand) operand;
FieldWrapper field = getField(codeContainer, fieldOperand);
instructions.add(new JavaInstruction(getJavaOpcodeForGet(field),
ConstantPoolEntryCreator.getOrCreateFieldRefEntry(getType(codeContainer, fieldOperand), field.getName(), field.getType())));
} else if (opcode == Opcode.INVOKE) {
assert operand instanceof MethodOperand;
var mo = (MethodOperand) operand;
if ("super".equals(mo.methodName)) {
instructions.add(new JavaInstruction(JavaOpcode.INVOKESPECIAL,
ConstantPoolEntryCreator.getOrCreateMethodRefEntry(beeSource.getSuperClass().getName(), "<init>", "()V")));
} else {
instructions.add(new JavaInstruction(JavaOpcode.INVOKEVIRTUAL, ConstantPoolEntryCreator.getOrCreateMethodRefEntry(mo.className, mo.methodName, mo.signature)));
}
System.out.println(operand);
} else if (operand instanceof FieldOperand) {
var fieldOperand = (FieldOperand) operand;
if (fieldOperand.className.equals("this")) {
instructions.add(new JavaInstruction(JavaOpcode.ALOAD_0));
}
if (opcode == Opcode.RETURN) {
instructions.add(getReturnInstruction(codeContainer, fieldOperand));
}
} else if (operand instanceof LocalVariableOperand) {
var localVariable = (LocalVariableOperand) operand;
instructions.add(codeContainer.getParameter(localVariable.name).map(this::getJavaLoadInstruction).orElse(null)); //else case
}
// TODO continue here. finish simpleBean
return instructions;
}
private JavaOpcode getJavaOpcodeForGet(FieldWrapper field) {
JavaOpcode get;
if (Modifier.isStatic(field.getModifiers())) {
get = JavaOpcode.GETSTATIC;
} else {
get = JavaOpcode.GETFIELD;
}
return get;
}
private JavaInstruction getReturnInstruction(CodeContainer codeContainer, FieldOperand fo) {
FieldWrapper field = getField(codeContainer, fo);
JavaOpcode javaOpcode = switch (field.getName()) {
case "int", "byte", "boolean", "short" -> JavaOpcode.IRETURN;
case "long" -> JavaOpcode.LRETURN;
case "float" -> JavaOpcode.FRETURN;
case "double" -> JavaOpcode.DRETURN;
default -> JavaOpcode.ARETURN;
};
return new JavaInstruction(javaOpcode);
}
private String getType(CodeContainer codeContainer, FieldOperand fo) {
if (fo.className.equals("this")) {
return codeContainer.getName();
} else {
return fo.className;
}
}
private FieldWrapper getField(CodeContainer codeContainer, FieldOperand fo) {
try {
Field javaField = null;
BeeField beeField = null;
if (fo.className.equals("this")) {
beeField = codeContainer.getOwner().getField(fo.fieldName);
} else {
Class<?> type = Class.forName(fo.className);
javaField = type.getDeclaredField(fo.fieldName);
}
return new FieldWrapper(javaField, beeField);
} catch (ClassNotFoundException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
private JavaInstruction getJavaLoadInstruction(BeeParameter parameter) {
return switch (parameter.getIndex()) {
case 0 -> new JavaInstruction(JavaOpcode.ALOAD_0);
case 1 -> new JavaInstruction(JavaOpcode.ALOAD_1);
case 2 -> new JavaInstruction(JavaOpcode.ALOAD_2);
case 3 -> new JavaInstruction(JavaOpcode.ALOAD_3);
default -> new JavaInstruction(JavaOpcode.ALOAD, parameter.getIndex());
};
}
}
class Parser {
Operand parse(CodeLine codeLine) {
var operand = codeLine.getOperand();
if (operand == null) {
return VoidOperand.INSTANCE;
} else {
if (operand.contains("(")) {
var parenIndex = operand.indexOf('(');
var signature = operand.substring(parenIndex);
var classAndMethodeName = operand.substring(0, parenIndex);
int lastDotIndex = classAndMethodeName.lastIndexOf('.');
var className = classAndMethodeName.substring(0, lastDotIndex);
var methodName = classAndMethodeName.substring(lastDotIndex + 1);
return new MethodOperand(className, methodName, signature);
} else {
int index = operand.lastIndexOf('.');
if (index < 0) {
var constantOperandOrNull = asConstantOperand(operand);
return Objects.requireNonNullElseGet(constantOperandOrNull, () -> new LocalVariableOperand(operand));
} else if (index < operand.length() - 1) {
var className = operand.substring(0, index);
var fieldName = operand.substring(index + 1);
return new FieldOperand(className, fieldName);
}
}
}
throw new IllegalArgumentException("compile error");
}
ConstantOperand asConstantOperand(String value) {
//TODO add boxed types
var tokens = value.split(" ");
var type = Primitive.from(tokens[0].toUpperCase(Locale.ROOT));
if (tokens.length == 2 && type != null) {
return new ConstantOperand(type, tokens[1]);
} else if (value.startsWith("\"")) {
if (value.endsWith("\"")) {
return new ConstantOperand(Primitive.STRING, value.substring(1, value.length() - 1));
} else {
throw new IllegalArgumentException("Unterminated String value");
}
}
return null;
}
}

View file

@ -0,0 +1,17 @@
package nl.sander.beejava;
import nl.sander.beejava.api.BeeSource;
public final class OverallCompiler {
private OverallCompiler(){
//
}
public static byte[] compile(String sourcecode){
BeeSource beeSource = SourceCompiler.compile(sourcecode);
OpcodeTranslator.translate(beeSource);
CompiledClass compiledClass = Compiler.compile(beeSource);
return BytecodeGenerator.generate(compiledClass);
}
}

View file

@ -0,0 +1,14 @@
package nl.sander.beejava;
import java.util.Arrays;
public enum Primitive {
INT, SHORT, BYTE, LONG, FLOAT, DOUBLE, CHAR, BOOLEAN, STRING;
public static Primitive from(String value) {
return Arrays.stream(values())
.filter(e -> e.toString().equals(value))
.findFirst()
.orElse(null);
}
}

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.apiv2; package nl.sander.beejava;
import nl.sander.beejava.api.Version; import nl.sander.beejava.api.*;
import nl.sander.beejava.flags.ClassAccessFlags; import nl.sander.beejava.flags.ClassAccessFlags;
import nl.sander.beejava.flags.FieldAccessFlag; import nl.sander.beejava.flags.FieldAccessFlag;
import nl.sander.beejava.flags.MethodAccessFlag; import nl.sander.beejava.flags.MethodAccessFlag;
@ -30,9 +30,11 @@ public class SourceCompiler {
} }
public BeeSource doCompile() { public BeeSource doCompile() {
Arrays.stream(sourcecode.split("\n")).map(this::compileLine).forEach(instructions::add); Arrays.stream(sourcecode.split("\n"))
.map(this::compileLine)
.forEach(instructions::add);
BeeSource beeSource = new nl.sander.beejava.apiv2.BeeSource(); BeeSource beeSource = new BeeSource();
for (currentLine = 0; currentLine < instructions.size(); ) { for (currentLine = 0; currentLine < instructions.size(); ) {
Instruction ins = instructions.get(currentLine); Instruction ins = instructions.get(currentLine);
@ -51,15 +53,15 @@ public class SourceCompiler {
ClassInstruction classInstruction = (ClassInstruction) instruction; ClassInstruction classInstruction = (ClassInstruction) instruction;
String operand = classInstruction.getOperand(); String operand = classInstruction.getOperand();
switch (classInstruction.getOperation()) { switch (classInstruction.getOperation()) {
case FIELD -> beeSource.addFields(parseField(operand)); case FIELD -> beeSource.addFields(parseField(beeSource, operand));
case CONSTRUCTOR -> beeSource.addConstructors(parseConstructor(operand)); case CONSTRUCTOR -> beeSource.addConstructors(parseConstructor(beeSource, operand));
case METHOD -> beeSource.addMethods(parseMethod(operand)); case METHOD -> beeSource.addMethods(parseMethod(beeSource, operand));
default -> throw new IllegalArgumentException("Not allowed here"); default -> throw new IllegalArgumentException("Not allowed here");
} }
} }
} }
private BeeMethod parseMethod(String text) { private nl.sander.beejava.api.BeeMethod parseMethod(BeeSource beeSource, String text) {
String[] tokens = returntypesplitter.split(text); String[] tokens = returntypesplitter.split(text);
final String first; final String first;
final Class<?> returnType; final Class<?> returnType;
@ -83,19 +85,19 @@ public class SourceCompiler {
} }
String[] nameParams = split(flagsNameParameters[i], parensplitter); String[] nameParams = split(flagsNameParameters[i], parensplitter);
Set<BeeParameter> parameters = new HashSet<>(); Set<nl.sander.beejava.api.BeeParameter> parameters = new HashSet<>();
String methodName = null; String methodName = null;
if (nameParams.length > 0) { if (nameParams.length > 0) {
methodName = nameParams[0]; methodName = nameParams[0];
if (nameParams[1].length() > 0) { if (nameParams[1].length() > 0) {
int index = 0;
String params = nameParams[1]; String params = nameParams[1];
String[] paramTokens = params.split(","); String[] paramTokens = params.split(",");
for (String paramToken : paramTokens) { for (String paramToken : paramTokens) {
String[] declaration = paramToken.trim().split(" "); String[] declaration = paramToken.trim().split(" ");
String type = declaration[0]; String type = declaration[0];
String name = declaration[1]; String name = declaration[1];
parameters.add(new BeeParameter(getType(type), name)); parameters.add(new nl.sander.beejava.api.BeeParameter(getType(type), name, index++));
} }
} }
} }
@ -103,22 +105,21 @@ public class SourceCompiler {
throw new IllegalArgumentException("method name not found"); throw new IllegalArgumentException("method name not found");
} }
currentLine += 1; currentLine += 1;
List<CodeLine> lines = new ArrayList<>(); List<nl.sander.beejava.api.CodeLine> lines = new ArrayList<>();
Instruction nextInstruction = instructions.get(currentLine); Instruction nextInstruction = instructions.get(currentLine);
while (nextInstruction instanceof CodeLine) { while (currentLine < instructions.size() && nextInstruction instanceof CodeLine) {
nextInstruction = instructions.get(currentLine);
if (nextInstruction instanceof CodeLine) {
lines.add((CodeLine) nextInstruction); lines.add((CodeLine) nextInstruction);
currentLine += 1; currentLine += 1;
if (currentLine >= instructions.size()) {
break; // too tired to think
} }
nextInstruction = instructions.get(currentLine);
} }
return new BeeMethod(methodName, flags, parameters, returnType, lines); return new BeeMethod(beeSource, methodName, flags, parameters, returnType, lines);
} }
private BeeConstructor parseConstructor(String text) { private nl.sander.beejava.api.BeeConstructor parseConstructor(BeeSource beeSource, String text) {
String[] tokens = split(text, parensplitter); String[] tokens = split(text, parensplitter);
String flag = tokens[0]; String flag = tokens[0];
MethodAccessFlag methodAccessFlag = MethodAccessFlag.get(flag.toUpperCase()).orElseThrow(illegalArgument("Not a valid flag " + flag)); MethodAccessFlag methodAccessFlag = MethodAccessFlag.get(flag.toUpperCase()).orElseThrow(illegalArgument("Not a valid flag " + flag));
@ -127,30 +128,29 @@ public class SourceCompiler {
Set<BeeParameter> parameters = new HashSet<>(); Set<BeeParameter> parameters = new HashSet<>();
if (params.length() > 0) { if (params.length() > 0) {
String[] paramTokens = params.split(","); String[] paramTokens = params.split(",");
int index = 0;
for (String paramToken : paramTokens) { for (String paramToken : paramTokens) {
String[] declaration = paramToken.trim().split(" "); String[] declaration = paramToken.trim().split(" ");
String type = declaration[0]; String type = declaration[0];
String name = declaration[1]; String name = declaration[1];
try { parameters.add(new BeeParameter(getType(type), name, index++));
parameters.add(new BeeParameter(Class.forName(type), name));
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("field type " + type + " not found");
}
} }
} }
currentLine += 1; currentLine += 1;
List<CodeLine> lines = new ArrayList<>(); List<CodeLine> lines = new ArrayList<>();
Instruction nextInstruction = instructions.get(currentLine); Instruction nextInstruction = instructions.get(currentLine);
while (nextInstruction instanceof CodeLine) { while (currentLine < instructions.size() && nextInstruction instanceof CodeLine) {
nextInstruction = instructions.get(currentLine);
if (nextInstruction instanceof CodeLine) {
lines.add((CodeLine) nextInstruction); lines.add((CodeLine) nextInstruction);
currentLine += 1; currentLine += 1;
nextInstruction = instructions.get(currentLine);
} }
return new BeeConstructor(Set.of(methodAccessFlag), parameters, lines);
} }
private BeeField parseField(String operand) { return new BeeConstructor(beeSource, Set.of(methodAccessFlag), parameters, lines);
}
private nl.sander.beejava.api.BeeField parseField(BeeSource beeSource, String operand) {
String[] tokens = operand.split(" "); String[] tokens = operand.split(" ");
Set<FieldAccessFlag> flags = new HashSet<>(); Set<FieldAccessFlag> flags = new HashSet<>();
String type = null; String type = null;
@ -168,19 +168,29 @@ public class SourceCompiler {
} }
} }
currentLine += 1; currentLine += 1;
return new BeeField(flags, getType(type), name); return new BeeField(beeSource.getName(), flags, getType(type), name);
} }
private void parseClassDeclaration(Instruction firstLine, BeeSource beeSource) { private void parseClassDeclaration(Instruction instruction, BeeSource beeSource) {
if (firstLine instanceof ClassInstruction) { if (instruction instanceof ClassInstruction) {
ClassInstruction classDeclaration = (ClassInstruction) firstLine; ClassInstruction classDeclaration = (ClassInstruction) instruction;
ClassOperation operation = classDeclaration.getOperation(); ClassOperation operation = classDeclaration.getOperation();
switch (operation) { switch (operation) {
case CLASS -> { case CLASS -> {
beeSource.addAccessFlags(ClassAccessFlags.SUPER);
beeSource.setClassFileVersion(getVersion(classDeclaration));
String[] tokens = split(classDeclaration.getOperand(), parensplitter); String[] tokens = split(classDeclaration.getOperand(), parensplitter);
beeSource.addAccessFlags(ClassAccessFlags.PUBLIC, ClassAccessFlags.SUPER); String rightHand = classDeclaration.getOperand();
beeSource.setName(tokens[0]); if (tokens.length > 0) { // has Version tag
beeSource.setClassFileVersion(Version.get(tokens[1]).orElseThrow(illegalArgument(tokens[1]))); rightHand = tokens[0];
}
tokens = rightHand.split(" ");
if (tokens.length == 2) { // has access flag
beeSource.addAccessFlags(ClassAccessFlags.valueOf(tokens[0].toUpperCase()));
}
beeSource.setName(getClassName(tokens));
} }
case INTERFACE -> { case INTERFACE -> {
}//TODO }//TODO
@ -194,6 +204,23 @@ public class SourceCompiler {
currentLine += 1; currentLine += 1;
} }
private Version getVersion(ClassInstruction classDeclaration) {
String[] tokens2 = split(classDeclaration.getOperand(), parensplitter);
if (tokens2.length == 0) {
return Version.V8;
} else {
return Version.get(tokens2[1]).orElseThrow(illegalArgument(tokens2[1]));
}
}
private String getClassName(String[] tokens) {
if (tokens.length == 2) {
return tokens[1];
} else {
return tokens[0];
}
}
private Instruction compileLine(String line) { private Instruction compileLine(String line) {
if (!line.startsWith(" ")) { if (!line.startsWith(" ")) {
String[] tokens = split(line, firstBlanksplitter); String[] tokens = split(line, firstBlanksplitter);
@ -203,6 +230,9 @@ public class SourceCompiler {
return new ClassInstruction(classOperation, operand); return new ClassInstruction(classOperation, operand);
} else { } else {
line = line.substring(2); line = line.substring(2);
if (line.startsWith(" ")) {
throw new IllegalArgumentException("Illegal indent -> must be 2 spaces");
}
String operation; String operation;
String operand; String operand;
if (line.indexOf(' ') > -1) { if (line.indexOf(' ') > -1) {
@ -236,7 +266,13 @@ public class SourceCompiler {
try { try {
return switch (type) { return switch (type) {
case "int" -> int.class; case "int" -> int.class;
case "short" -> short.class;
case "byte" -> byte.class;
case "long" -> long.class;
case "float" -> float.class;
case "double" -> double.class; case "double" -> double.class;
case "boolean" -> boolean.class;
case "char" -> char.class;
default -> Class.forName(type); default -> Class.forName(type);
}; };
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {

View file

@ -16,8 +16,7 @@ public class TypeMapper {
MAP.put(long.class, "J"); MAP.put(long.class, "J");
MAP.put(short.class, "S"); MAP.put(short.class, "S");
MAP.put(boolean.class, "Z"); MAP.put(boolean.class, "Z");
MAP.put(Void.class, "V"); MAP.put(void.class, "V");
} }
//TODO something with arrays //TODO something with arrays

View file

@ -9,19 +9,15 @@ import java.util.*;
*/ */
public final class BeeConstructor extends CodeContainer { public final class BeeConstructor extends CodeContainer {
public BeeConstructor(Set<MethodAccessFlag> accessFlags, public BeeConstructor(BeeSource beeSource, Set<MethodAccessFlag> accessFlags,
Set<BeeParameter> formalParameters, Set<BeeParameter> formalParameters,
List<CodeLine> code) { List<CodeLine> code) {
this.formalParameters.addAll(formalParameters); this.formalParameters.addAll(formalParameters);
this.accessFlags.addAll(accessFlags); this.accessFlags.addAll(accessFlags);
super.code.addAll(code); super.code.addAll(code);
setOwner(beeSource);
} }
public static Builder builder() {
return new Builder();
}
public String getName() { public String getName() {
return "<init>"; return "<init>";
} }
@ -34,7 +30,7 @@ public final class BeeConstructor extends CodeContainer {
} }
public Class<?> getReturnType() { public Class<?> getReturnType() {
return Void.class; return void.class;
} }
@Override @Override
@ -55,34 +51,4 @@ public final class BeeConstructor extends CodeContainer {
return Objects.hash(formalParameters); return Objects.hash(formalParameters);
} }
public static class Builder {
private final Set<MethodAccessFlag> accessFlags = new HashSet<>();
private final Set<BeeParameter> formalParameters = new HashSet<>();
private final List<CodeLine> code = new LinkedList<>();
private Builder() {
}
public Builder withFormalParameters(BeeParameter... formalParameters) {
this.formalParameters.addAll(Arrays.asList(formalParameters));
return this;
}
public Builder withAccessFlags(MethodAccessFlag... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public Builder withCode(CodeLine... lines) {
this.code.addAll(Arrays.asList(lines));
return this;
}
public BeeConstructor build() {
BeeConstructor beeConstructor = new BeeConstructor(accessFlags, formalParameters, code);
code.forEach(line -> line.setOwner(beeConstructor));
return beeConstructor;
}
}
} }

View file

@ -2,7 +2,6 @@ package nl.sander.beejava.api;
import nl.sander.beejava.flags.FieldAccessFlag; import nl.sander.beejava.flags.FieldAccessFlag;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
@ -12,20 +11,25 @@ import java.util.Set;
*/ */
public final class BeeField { public final class BeeField {
private final String declaringClass;
private final Set<FieldAccessFlag> accessFlags = new HashSet<>(); private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
private final Class<?> type; private final Class<?> type;
private final String name; private final String name;
public BeeField(Set<FieldAccessFlag> accessFlags, Class<?> type, String name) { /**
*
* @param declaringClass class that declares the field. Can be existing class, or the one that is under construction
* @param accessFlags
* @param type field type. Must be existing type.
* @param name
*/
public BeeField(String declaringClass, Set<FieldAccessFlag> accessFlags, Class<?> type, String name) {
this.declaringClass = declaringClass;
this.accessFlags.addAll(accessFlags); this.accessFlags.addAll(accessFlags);
this.type = type; this.type = type;
this.name = name; this.name = name;
} }
public static BeeField.Builder builder(){
return new Builder();
}
public Set<FieldAccessFlag> getAccessFlags() { public Set<FieldAccessFlag> getAccessFlags() {
return accessFlags; return accessFlags;
} }
@ -38,6 +42,10 @@ public final class BeeField {
return name; return name;
} }
public String getDeclaringClass() {
return declaringClass;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@ -51,32 +59,5 @@ public final class BeeField {
return Objects.hash(name); return Objects.hash(name);
} }
public static class Builder {
private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
private Class<?> type;
private String name;
private Builder(){
} }
public BeeField.Builder withAccessFlags(FieldAccessFlag... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public BeeField.Builder withType(Class<?> type) {
this.type=type;
return this;
}
public BeeField.Builder withName(String name) {
this.name=name;
return this;
}
public BeeField build() {
return new BeeField(accessFlags, type, name);
}
}
}

View file

@ -12,18 +12,15 @@ public final class BeeMethod extends CodeContainer {
private final Class<?> returnType; private final Class<?> returnType;
private BeeMethod(String name, Set<MethodAccessFlag> accessFlags, public BeeMethod(BeeSource beeSource, String name, Set<MethodAccessFlag> accessFlags,
List<BeeParameter> formalParameters, Set<BeeParameter> formalParameters,
Class<?> returnType, List<CodeLine> code) { Class<?> returnType, List<CodeLine> code) {
this.name = name; this.name = name;
this.accessFlags.addAll(accessFlags); this.accessFlags.addAll(accessFlags);
this.formalParameters.addAll(formalParameters); this.formalParameters.addAll(formalParameters);
this.returnType = returnType; this.returnType = returnType;
super.code.addAll(code); super.code.addAll(code);
} setOwner(beeSource);
public static Builder builder() {
return new Builder();
} }
public String getName() { public String getName() {
@ -48,45 +45,18 @@ public final class BeeMethod extends CodeContainer {
*/ */
} }
public static class Builder { @Override
private final Set<MethodAccessFlag> accessFlags = new HashSet<>(); public boolean equals(Object o) {
private final List<BeeParameter> formalParameters = new LinkedList<>(); if (this == o) return true;
private final List<CodeLine> code = new LinkedList<>(); if (o == null || getClass() != o.getClass()) return false;
private String name; if (!super.equals(o)) return false;
private Class<?> returnType = Void.class; BeeMethod beeMethod = (BeeMethod) o;
return name.equals(beeMethod.name) &&
private Builder() { returnType.equals(beeMethod.returnType);
} }
public Builder withName(String name) { @Override
this.name = name; public int hashCode() {
return this; return Objects.hash(super.hashCode(), name, returnType);
}
public Builder withAccessFlags(MethodAccessFlag... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public Builder withFormalParameters(BeeParameter... formalParameters) {
this.formalParameters.addAll(Arrays.asList(formalParameters));
return this;
}
public Builder withReturnType(Class<?> returnType) {
this.returnType = returnType;
return this;
}
public Builder withCode(CodeLine... lines) {
this.code.addAll(Arrays.asList(lines));
return this;
}
public BeeMethod build() {
BeeMethod beeMethod = new BeeMethod(name, accessFlags, formalParameters, returnType, code);
code.forEach(line -> line.setOwner(beeMethod));
return beeMethod;
}
} }
} }

View file

@ -1,17 +0,0 @@
package nl.sander.beejava.api;
/**
* Contains the name of the package for a class
*/
public final class BeePackage {
private final String name;
BeePackage(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View file

@ -8,14 +8,18 @@ import java.util.Objects;
public final class BeeParameter { public final class BeeParameter {
private final Class<?> type; private final Class<?> type;
private final String name; private final String name;
private final int index;
public BeeParameter(Class<?> type, String name) { public BeeParameter(Class<?> type, String name) {
this.type = type; this.type = type;
this.name = name; this.name = name;
this.index = -1;
} }
public static BeeParameter create(Class<?> type, String name) { public BeeParameter(Class<?> type, String name, int index) {
return new BeeParameter(type, Objects.requireNonNull(name)); this.type = type;
this.name = name;
this.index = index;
} }
public Class<?> getType() { public Class<?> getType() {
@ -34,6 +38,10 @@ public final class BeeParameter {
return name.equals(that.name); return name.equals(that.name);
} }
public int getIndex() {
return index;
}
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(name); return Objects.hash(name);

View file

@ -2,50 +2,22 @@ package nl.sander.beejava.api;
import nl.sander.beejava.flags.ClassAccessFlags; import nl.sander.beejava.flags.ClassAccessFlags;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
/** /**
* Contains all information needed for compilation * Contains parsed class elements like constructors and methods, but the opcode is not compiled to bytecode yet.
*
* End users need to create an instance of this class (using the Builder) and add all fields, methods etc.
* Once created the BeeSource object is immutable.
*/ */
public final class BeeSource { public final class BeeSource {
private final Version classFileVersion;
private final BeePackage beePackage;
private final Set<ClassAccessFlags> accessFlags = new HashSet<>(); private final Set<ClassAccessFlags> accessFlags = new HashSet<>();
private final String simpleName;
private final Class<?> superClass;
private final Set<Class<?>> interfaces = new HashSet<>(); private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeeField> fields = new HashSet<>(); private final Set<BeeField> fields = new HashSet<>();
private final Set<BeeConstructor> constructors = new HashSet<>(); private final Set<BeeConstructor> constructors = new HashSet<>();
private final Set<BeeMethod> methods = new HashSet<>(); private final Set<BeeMethod> methods = new HashSet<>();
private Version classFileVersion;
private BeeSource(Version classFileVersion, private String name;
BeePackage beePackage, Set<ClassAccessFlags> accessFlags, String simpleName, Class<?> superClass, private Class<?> superClass = Object.class;
Set<Class<?>> interfaces, Set<BeeField> fields, Set<BeeConstructor> constructors, Set<BeeMethod> methods) {
this.classFileVersion = classFileVersion;
this.beePackage = beePackage;
this.accessFlags.addAll(accessFlags);
this.simpleName = simpleName;
this.superClass = superClass;
this.interfaces.addAll(interfaces);
this.fields.addAll(fields);
this.constructors.addAll(constructors);
this.methods.addAll(methods);
}
/**
* Create a new BeeSource Builder class.
*
* @return a new instance of a Builder
*/
public static BeeSource.Builder builder() {
return new Builder();
}
/** /**
* @return The classfile version * @return The classfile version
@ -54,18 +26,8 @@ public final class BeeSource {
return classFileVersion; return classFileVersion;
} }
/** public void setClassFileVersion(Version classFileVersion) {
* @return The package in which the compiled class will reside. this.classFileVersion = classFileVersion;
*/
public BeePackage getPackage() {
return beePackage;
}
/**
* returns the unqualified name, like java.lang.Class
*/
public String getSimpleName() {
return simpleName;
} }
/** /**
@ -75,6 +37,10 @@ public final class BeeSource {
return Collections.unmodifiableSet(constructors); return Collections.unmodifiableSet(constructors);
} }
public void addConstructors(BeeConstructor... constructors) {
this.constructors.addAll(Set.of(constructors));
}
/** /**
* @return all methods that are provided with the class * @return all methods that are provided with the class
*/ */
@ -82,6 +48,10 @@ public final class BeeSource {
return methods; return methods;
} }
public void addMethods(BeeMethod... methods) {
this.methods.addAll(Set.of(methods));
}
/** /**
* @return The access flags for the class * @return The access flags for the class
*/ */
@ -89,11 +59,19 @@ public final class BeeSource {
return Collections.unmodifiableSet(accessFlags); return Collections.unmodifiableSet(accessFlags);
} }
public void addAccessFlags(ClassAccessFlags... classAccessFlags) {
this.accessFlags.addAll(Set.of(classAccessFlags));
}
/** /**
* @return The full name, like java.lang.Class * @return The full name, like java.lang.Class
*/ */
public String getName() { public String getName() {
return beePackage.getName() + "." + simpleName; return name;
}
public void setName(String name) {
this.name = name;
} }
/** /**
@ -103,6 +81,10 @@ public final class BeeSource {
return superClass; return superClass;
} }
public void setSuperClass(Class<?> superClass) {
this.superClass = superClass;
}
/** /**
* @return a list of unique interfaces that the class will implements * @return a list of unique interfaces that the class will implements
*/ */
@ -110,6 +92,10 @@ public final class BeeSource {
return Collections.unmodifiableSet(interfaces); return Collections.unmodifiableSet(interfaces);
} }
public void addInterfaces(Class<?>... interfaces) {
this.interfaces.addAll(Set.of(interfaces));
}
/** /**
* @return a list of unique fields that the class will contain * @return a list of unique fields that the class will contain
*/ */
@ -117,74 +103,14 @@ public final class BeeSource {
return Collections.unmodifiableSet(fields); return Collections.unmodifiableSet(fields);
} }
/** public void addFields(BeeField... fields) {
* Helper class for creating BeeSource classes this.fields.addAll(Set.of(fields));
*/
public static class Builder {
private final Set<ClassAccessFlags> accessFlags = new HashSet<>();
private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeeField> fields = new HashSet<>();
private Version version;
private BeePackage beePackage;
private Class<?> superClass = Object.class;
private String simpleName;
private final Set<BeeConstructor> constructors = new HashSet<>();
private final Set<BeeMethod> methods = new HashSet<>();
private Builder() {
}
public Builder withClassFileVersion(Version version) {
this.version = version;
return this;
}
public BeeSource.Builder withPackage(String beePackage) {
this.beePackage = new BeePackage(beePackage);
return this;
}
public BeeSource.Builder withAccessFlags(ClassAccessFlags... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public BeeSource.Builder withSimpleName(String simpleName) {
this.simpleName = simpleName;
return this;
}
public Builder withSuperClass(Class<?> superClass) {
this.superClass = superClass;
return this;
}
public Builder withInterfaces(Class<?>... interfaces) {
this.interfaces.addAll(Arrays.asList(interfaces));
return this;
}
public Builder withFields(BeeField... fields) {
this.fields.addAll(Arrays.asList(fields));
return this;
}
public Builder withConstructors(BeeConstructor... constructors) {
this.constructors.addAll(Arrays.asList(constructors));
return this;
}
public Builder withMethods(BeeMethod... methods) {
this.methods.addAll(Arrays.asList(methods));
return this;
}
public BeeSource build() {
BeeSource beeSource = new BeeSource(version, beePackage, accessFlags, simpleName, superClass, interfaces, fields, constructors, methods);
constructors.forEach(c -> c.setOwner(beeSource));
methods.forEach(m -> m.setOwner(beeSource));
return beeSource;
} }
public BeeField getField(String fieldName) {
return fields.stream()
.filter(f -> f.getName().equals(fieldName))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("field " + fieldName + " not found in " + getName()));
} }
} }

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.apiv2; package nl.sander.beejava.api;
public class ClassInstruction extends Instruction { public class ClassInstruction extends Instruction {
private final ClassOperation classOperation; private final ClassOperation classOperation;

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.apiv2; package nl.sander.beejava.api;
import java.util.Optional; import java.util.Optional;
@ -10,7 +10,7 @@ public enum ClassOperation {
CONSTRUCTOR, CONSTRUCTOR,
METHOD; METHOD;
static Optional<ClassOperation> get(String text){ public static Optional<ClassOperation> get(String text){
String upper = text.toUpperCase(); String upper = text.toUpperCase();
for (ClassOperation val: ClassOperation.values()){ for (ClassOperation val: ClassOperation.values()){
if (val.toString().equals(upper)){ if (val.toString().equals(upper)){

View file

@ -1,9 +1,7 @@
package nl.sander.beejava.api; package nl.sander.beejava.api;
import nl.sander.beejava.JavaInstruction;
import nl.sander.beejava.TypeMapper; import nl.sander.beejava.TypeMapper;
import nl.sander.beejava.api.BeeParameter;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.CodeLine;
import nl.sander.beejava.flags.MethodAccessFlag; import nl.sander.beejava.flags.MethodAccessFlag;
import java.util.*; import java.util.*;
@ -17,6 +15,7 @@ public abstract class CodeContainer {
protected final List<CodeLine> code = new LinkedList<>(); protected final List<CodeLine> code = new LinkedList<>();
protected final Set<BeeParameter> formalParameters = new HashSet<>(); protected final Set<BeeParameter> formalParameters = new HashSet<>();
protected final Set<MethodAccessFlag> accessFlags = new HashSet<>(); protected final Set<MethodAccessFlag> accessFlags = new HashSet<>();
private final List<JavaInstruction> expandedCode = new ArrayList<>();
private BeeSource owner; private BeeSource owner;
public List<CodeLine> getCode() { public List<CodeLine> getCode() {
@ -53,9 +52,35 @@ public abstract class CodeContainer {
this.owner = beeSource; this.owner = beeSource;
} }
public List<JavaInstruction> getExpandedCode() {
return Collections.unmodifiableList(expandedCode);
}
public void setExpandedCode(List<JavaInstruction> instructions) {
expandedCode.clear();
expandedCode.addAll(instructions);
}
public abstract boolean isConstructor(); public abstract boolean isConstructor();
public Set<BeeParameter> getFormalParameters() { public Set<BeeParameter> getFormalParameters() {
return formalParameters; return formalParameters;
} }
public Optional<BeeParameter> getParameter(String parameterName) {
return formalParameters.stream().filter(p -> parameterName.equals(p.getName())).findAny();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CodeContainer that = (CodeContainer) o;
return formalParameters.equals(that.formalParameters);
}
@Override
public int hashCode() {
return Objects.hash(formalParameters);
}
} }

View file

@ -1,249 +1,28 @@
package nl.sander.beejava.api; package nl.sander.beejava.api;
import nl.sander.beejava.JavaOpcode; public class CodeLine extends Instruction {
import nl.sander.beejava.TypeMapper;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public final class CodeLine {
private final Opcode opcode; private final Opcode opcode;
private Ref ref;
private BeeParameter parameter;
private Class<?> type;
private String methodName;
private List<Class<?>> inputSignature;
private String outputSignature;
private BeeField ownfield; // when you create a class with a field, you can refer to it
private Field externalfield; // when you refer to a field from another class
private Object constValue;
private ConstantPoolEntry assignedEntry;
private CodeContainer owner; private CodeContainer owner;
private JavaOpcode javaOpcode;
CodeLine(Opcode opcode) { public CodeLine(Opcode opcode, String operand) {
super(operand);
this.opcode = opcode; this.opcode = opcode;
} }
public static CodeLine line(Opcode opcode, Ref ref) { public String getOperand() {
return new CodeLine(opcode).withRef(ref); return operand;
}
public static CodeLine line(Opcode opcode, String fieldClass, String fieldName) {
return new CodeLine(opcode).withExternalFieldRef(fieldClass, fieldName);
}
public static CodeLine line(Opcode opcode, String constValue) {
return new CodeLine(opcode).withConstValue(constValue);
}
public static CodeLine line(Opcode opcode, String className, String methodName, String inputSignature) throws ClassNotFoundException {
return new CodeLine(opcode).withClassName(className).withMethodName(methodName).withInput(parse(inputSignature)).withVoidOutput();
}
public static CodeLine line(Opcode opcode,
Ref ref, String methodNameRef, String inputSignature) throws ClassNotFoundException {
return new CodeLine(opcode).withRef(ref).withMethodName(methodNameRef).withInput(parse(inputSignature)).withVoidOutput();
}
public static CodeLine line(Opcode opcode,
Ref ref, String methodNameRef, String inputSignature, String outputSignature) throws ClassNotFoundException {
return new CodeLine(opcode).withRef(ref).withMethodName(methodNameRef).withInput(parse(inputSignature)).withOutput(outputSignature);
}
public static CodeLine line(Opcode opcode) {
return new CodeLine(opcode);
}
public static CodeLine line(Opcode opcode, BeeParameter parameter) {
return new CodeLine(opcode).withParameter(parameter);
}
public static CodeLine line(Opcode opcode, BeeField intField) {
return new CodeLine(opcode).withRef(Ref.THIS).withOwnField(intField);
}
private CodeLine withRef(Ref ref) {
this.ref = ref;
return this;
}
private CodeLine withClassName(String className) {
this.type = loadClass(className);
return this;
}
private CodeLine withMethodName(String methodName) {
this.methodName = methodName;
return this;
}
private CodeLine withInput(List<Class<?>> inputSignature) {
this.inputSignature = inputSignature;
return this;
}
private CodeLine withVoidOutput() {
return withOutput("V");
}
private CodeLine withOutput(String outputSignature) {
this.outputSignature = outputSignature;
return this;
}
private CodeLine withParameter(BeeParameter parameter) {
this.parameter = parameter;
return this;
}
private CodeLine withConstValue(Object constValue) {
this.constValue = constValue;
return this; //TODO
}
private CodeLine withOwnField(BeeField beeField) {
this.ownfield = beeField;
return this;
}
private CodeLine withExternalFieldRef(String className, String field) {
this.type = loadClass(className);
this.externalfield = loadField(field);
return this;
}
// TODO decide whether to work with Strings or class objects...
/*
* Look up the type of a field in a class
*
* Assumes field is accessible
*
* @param className the class containing a field
* @param field name of the field
* @return the field type
*/
private Field loadField(String field) {
try {
return type.getField(field);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
}
public boolean hasClassName() {
return type != null;
}
public String getClassName() {
return type.getName();
}
public String getMethodName() {
return methodName;
}
public boolean hasMethodCall() {
return methodName != null;
}
public String getMethodSignature() {
return inputSignature.stream()
.map(TypeMapper::map)
.collect(Collectors.joining(",", "(", ")")) + outputSignature;
}
public Ref getRef() {
return ref;
}
public boolean hasRef() {
return ref != null;
}
public boolean hasRefToOwnField() {
return ownfield != null;
}
public Object getConstValue() {
return constValue;
}
public boolean hasConstValue() {
return constValue != null;
}
public BeeField getOwnfield() {
return ownfield;
}
public BeeParameter getParameter() {
return parameter;
}
public boolean hasRefToExternalField() {
return externalfield != null;
}
public Field getExternalfield() {
return externalfield;
}
public ConstantPoolEntry getAssignedEntry() {
return assignedEntry;
}
public void setAssignedEntry(ConstantPoolEntry assignedEntry) {
this.assignedEntry = assignedEntry;
} }
public Opcode getOpcode() { public Opcode getOpcode() {
return opcode; return opcode;
} }
private Class<?> loadClass(String className) { public void setOwner(CodeContainer owner) {
try { this.owner = owner;
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e); //TODO specific exception
}
} }
public CodeContainer getOwner() { public CodeContainer getOwner() {
return owner; return owner;
} }
public void setOwner(CodeContainer codeContainer) {
this.owner = codeContainer;
}
public JavaOpcode getJavaOpcode() {
return javaOpcode;
}
public void setJavaOpcode(JavaOpcode javaOpcode) {
this.javaOpcode = javaOpcode;
}
private static List<Class<?>> parse(String inputSignature) throws ClassNotFoundException {
if ("()".equals(inputSignature)) {
return Collections.emptyList();
} else {
String[] params = inputSignature.split(",");
List<Class<?>> paramClasses = new ArrayList<>();
for (String param : params) {
paramClasses.add(Class.forName(param));
}
return paramClasses;
}
}
} }

View file

@ -1,4 +1,4 @@
package nl.sander.beejava.apiv2; package nl.sander.beejava.api;
public class Instruction { public class Instruction {
protected final String operand; protected final String operand;

View file

@ -1,71 +1,65 @@
package nl.sander.beejava.api; package nl.sander.beejava.api;
import java.util.Optional;
public enum Opcode { public enum Opcode {
LD_VAR("ld_var"), LOAD,
LD_CONST("ld_const"), STORE,
LD_FIELD("ld_field"), CONST,
STORE("store"), RETURN,
CONST("const"), ARRAYLENGTH,
NEWARRAY("new"), THROW,
RETURN("return"), CAST,
ARRAYLENGTH("length"), ADD,
THROW("throw"), COMPARE,
PUSH("push"), DIVIDE,
CHECKCAST("checkcast"), MULTIPLY,
ADD("add"), NEGATE,
COMPARE("cmp"), REMAINDER,
DIVIDE("div"), SUBTRACT,
MULTIPLY("mul"), GET,
NEGATE("neg"), GOTO,
REMAINDER("rem"), TO_DOUBLE,
SUBTRACT("sub"), TO_INT,
DUPLICATE("dup"), TO_LONG,
DUPLICATE_DOWN("dup_x1"), TO_BYTE,
DUPLICATE2("dup2"), TO_CHAR,
DUPLICATE2_DOWN("dup2_x1"), TO_FLOAT,
GET("get"), TO_SHORT,
GOTO("goto"), AND,
GOTO_W("goto_w"), IF_EQUAL,
TO_DOUBLE("2d"), IF_NOT_EQUAL,
TO_INT("2i"), IF_LESS_THAN,
TO_LONG("2l"), IF_GREATER_OR_EQUAL,
TO_BYTE("2b"), IF_GREATER_THAN,
TO_CHAR("2c"), IF_LESS_OR_EQUAL,
TO_FLOAT("2f"), IF_NOT_NULL,
TO_SHORT("2s"), IF_NULL,
AND("and"), INCREMENT,
IF_EQUAL("ifeq"), INSTANCEOF,
IF_NOT_EQUAL("ifneq"), INVOKE,
IF_LESS_THAN("iflt"), OR,
IF_GREATER_OR_EQUAL("ifge"), SHIFT_LEFT,
IF_GREATER_THAN("ifgt"), SHIFT_RIGHT,
IF_LESS_OR_EQUAL("ifle"), LOGICAL_SHIFT_RIGHT,
IF_NOT_NULL("ifnotnull"), XOR,
IF_NULL("ifnull"), LOOKUPSWITCH,
INCREMENT("inc"), MONITORENTER,
INSTANCEOF("instanceof"), MONITOREXIT,
INVOKE("invoke"), NEW,
OR("or"), NEWARRAY,
SHIFT_LEFT("shr"), MULTIANEWARRAY,
SHIFT_RIGHT("shl"), PUT,
LOGICAL_SHIFT_RIGHT("ushr"), SWAP,
XOR("xor"), TABLESWITCH,
LOOKUPSWITCH("lookupswitch"), WIDE;
MONITORENTER("monitorenter"),
MONITOREXIT("monitorexit"),
MULTIANEWARRAY("multinewarray"),
NEW("new"),
NOP("nop"),
POP("pop"),
POP2("pop2"),
PUT("put"),
SWAP("swap"),
TABLESWITCH("tableswitch"),
WIDE("wide");
private final String name; public static Optional<Opcode> get(String text) {
for (Opcode opcode : Opcode.values()) {
Opcode(String name) { if (opcode.toString().equals(text)) {
this.name=name; return Optional.of(opcode);
}
}
return Optional.empty();
} }
} }

View file

@ -1,9 +0,0 @@
package nl.sander.beejava.api;
/**
* used to indicate this, super, or none of those, in which case it's class
*/
public enum Ref {
THIS,
SUPER
}

View file

@ -1,6 +1,6 @@
package nl.sander.beejava.api; package nl.sander.beejava.api;
import nl.sander.beejava.apiv2.Opcode; import nl.sander.beejava.api.Opcode;
import java.util.Optional; import java.util.Optional;

View file

@ -1,53 +0,0 @@
package nl.sander.beejava.apiv2;
import nl.sander.beejava.flags.MethodAccessFlag;
import java.util.*;
/**
* Models a constructor
*/
public final class BeeConstructor extends CodeContainer {
public BeeConstructor(Set<MethodAccessFlag> accessFlags,
Set<BeeParameter> formalParameters,
List<CodeLine> code) {
this.formalParameters.addAll(formalParameters);
this.accessFlags.addAll(accessFlags);
super.code.addAll(code);
}
public String getName() {
return "<init>";
}
@Override
public String toString() {
return "BeeConstructor{" +
"formalParameters=" + formalParameters +
'}';
}
public Class<?> getReturnType() {
return Void.class;
}
@Override
public boolean isConstructor() {
return true;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeConstructor that = (BeeConstructor) o;
return formalParameters.equals(that.formalParameters);
}
@Override
public int hashCode() {
return Objects.hash(formalParameters);
}
}

View file

@ -1,82 +0,0 @@
package nl.sander.beejava.apiv2;
import nl.sander.beejava.flags.FieldAccessFlag;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* Models a field in a BeeClass
*/
public final class BeeField {
private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
private final Class<?> type;
private final String name;
public BeeField(Set<FieldAccessFlag> accessFlags, Class<?> type, String name) {
this.accessFlags.addAll(accessFlags);
this.type = type;
this.name = name;
}
public static Builder builder(){
return new Builder();
}
public Set<FieldAccessFlag> getAccessFlags() {
return accessFlags;
}
public Class<?> getType() {
return type;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeField beeField = (BeeField) o;
return name.equals(beeField.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
public static class Builder {
private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
private Class<?> type;
private String name;
private Builder(){
}
public Builder withAccessFlags(FieldAccessFlag... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public Builder withType(Class<?> type) {
this.type=type;
return this;
}
public Builder withName(String name) {
this.name=name;
return this;
}
public BeeField build() {
return new BeeField(accessFlags, type, name);
}
}
}

View file

@ -1,61 +0,0 @@
package nl.sander.beejava.apiv2;
import nl.sander.beejava.flags.MethodAccessFlag;
import java.util.*;
/**
* Models a method in a BeeClass
*/
public final class BeeMethod extends CodeContainer {
private final String name;
private final Class<?> returnType;
public BeeMethod(String name, Set<MethodAccessFlag> accessFlags,
Set<BeeParameter> formalParameters,
Class<?> returnType, List<CodeLine> code) {
this.name = name;
this.accessFlags.addAll(accessFlags);
this.formalParameters.addAll(formalParameters);
this.returnType = returnType;
super.code.addAll(code);
}
public String getName() {
return name;
}
public Class<?> getReturnType() {
return returnType;
}
@Override
public boolean isConstructor() {
return false;
}
public void validate() {
//TODO
/*
* here we could add checks like:
* -If this method is in a class rather than an interface, and the name of the method is <init>, then the descriptor must denote a void method.
* -If the name of the method is <clinit>, then the descriptor must denote avoid method, and, in a class file whose version number is 51.0 or above,a method that takes no arguments
*/
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
BeeMethod beeMethod = (BeeMethod) o;
return name.equals(beeMethod.name) &&
returnType.equals(beeMethod.returnType);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), name, returnType);
}
}

View file

@ -1,37 +0,0 @@
package nl.sander.beejava.apiv2;
import java.util.Objects;
/**
* Models a formal parameter in a method declaration.
*/
public final class BeeParameter {
private final Class<?> type;
private final String name;
public BeeParameter(Class<?> type, String name) {
this.type = type;
this.name = name;
}
public Class<?> getType() {
return type;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeParameter that = (BeeParameter) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}

View file

@ -1,110 +0,0 @@
package nl.sander.beejava.apiv2;
import nl.sander.beejava.api.Version;
import nl.sander.beejava.flags.ClassAccessFlags;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
* Contains parsed class elements like constructors and methods, but the opcode is not compiled to bytecode yet.
*/
public final class BeeSource {
private final Set<ClassAccessFlags> accessFlags = new HashSet<>();
private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeeField> fields = new HashSet<>();
private final Set<BeeConstructor> constructors = new HashSet<>();
private final Set<BeeMethod> methods = new HashSet<>();
private Version classFileVersion;
private String name;
private Class<?> superClass;
/**
* @return The classfile version
*/
public Version getClassFileVersion() {
return classFileVersion;
}
public void setClassFileVersion(Version classFileVersion) {
this.classFileVersion = classFileVersion;
}
/**
* @return all constructors that are provided with the class
*/
public Set<BeeConstructor> getConstructors() {
return Collections.unmodifiableSet(constructors);
}
public void addConstructors(BeeConstructor... constructors) {
this.constructors.addAll(Set.of(constructors));
}
/**
* @return all methods that are provided with the class
*/
public Set<BeeMethod> getMethods() {
return methods;
}
public void addMethods(BeeMethod... methods) {
this.methods.addAll(Set.of(methods));
}
/**
* @return The access flags for the class
*/
public Set<ClassAccessFlags> getAccessFlags() {
return Collections.unmodifiableSet(accessFlags);
}
public void addAccessFlags(ClassAccessFlags... classAccessFlags) {
this.accessFlags.addAll(Set.of(classAccessFlags));
}
/**
* @return The full name, like java.lang.Class
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @return The superclass
*/
public Class<?> getSuperClass() {
return superClass;
}
public void setSuperClass(Class<?> superClass) {
this.superClass = superClass;
}
/**
* @return a list of unique interfaces that the class will implements
*/
public Set<Class<?>> getInterfaces() {
return Collections.unmodifiableSet(interfaces);
}
public void addInterfaces(Class<?>... interfaces) {
this.interfaces.addAll(Set.of(interfaces));
}
/**
* @return a list of unique fields that the class will contain
*/
public Set<BeeField> getFields() {
return Collections.unmodifiableSet(fields);
}
public void addFields(BeeField... fields) {
this.fields.addAll(Set.of(fields));
}
}

View file

@ -1,67 +0,0 @@
package nl.sander.beejava.apiv2;
import nl.sander.beejava.TypeMapper;
import nl.sander.beejava.flags.MethodAccessFlag;
import java.util.*;
import java.util.stream.Collectors;
/**
* parent of a constructor or a method.
*/
public abstract class CodeContainer {
protected final List<CodeLine> code = new LinkedList<>();
protected final Set<BeeParameter> formalParameters = new HashSet<>();
protected final Set<MethodAccessFlag> accessFlags = new HashSet<>();
private BeeSource owner;
public List<CodeLine> getCode() {
return code;
}
public String getSignature() {
return getParametersSignature() + TypeMapper.map(getReturnType());
}
public abstract String getName();
public abstract Class<?> getReturnType();
private String getParametersSignature() {
return formalParameters.stream()
.map(BeeParameter::getType)
.map(TypeMapper::map)
.collect(Collectors.joining(",", "(", ")"));
}
public Set<MethodAccessFlag> getAccessFlags() {
return accessFlags;
}
public BeeSource getOwner() {
return owner;
}
public void setOwner(BeeSource beeSource) {
if (owner != null) {
throw new IllegalStateException("Owner set twice. Sue the developer!");
}
this.owner = beeSource;
}
public abstract boolean isConstructor();
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CodeContainer that = (CodeContainer) o;
return formalParameters.equals(that.formalParameters);
}
@Override
public int hashCode() {
return Objects.hash(formalParameters);
}
}

View file

@ -1,28 +0,0 @@
package nl.sander.beejava.apiv2;
public class CodeLine extends Instruction {
private final Opcode opcode;
private CodeContainer owner;
public CodeLine(Opcode opcode, String operand) {
super(operand);
this.opcode = opcode;
}
public String getOperand() {
return operand;
}
public Opcode getOpcode() {
return opcode;
}
public void setOwner(CodeContainer owner) {
this.owner = owner;
}
public CodeContainer getOwner() {
return owner;
}
}

View file

@ -1,65 +0,0 @@
package nl.sander.beejava.apiv2;
import java.util.Optional;
public enum Opcode {
LOAD,
STORE,
CONST,
RETURN,
ARRAYLENGTH,
THROW,
CAST,
ADD,
COMPARE,
DIVIDE,
MULTIPLY,
NEGATE,
REMAINDER,
SUBTRACT,
GET,
GOTO,
TO_DOUBLE,
TO_INT,
TO_LONG,
TO_BYTE,
TO_CHAR,
TO_FLOAT,
TO_SHORT,
AND,
IF_EQUAL,
IF_NOT_EQUAL,
IF_LESS_THAN,
IF_GREATER_OR_EQUAL,
IF_GREATER_THAN,
IF_LESS_OR_EQUAL,
IF_NOT_NULL,
IF_NULL,
INCREMENT,
INSTANCEOF,
INVOKE,
OR,
SHIFT_LEFT,
SHIFT_RIGHT,
LOGICAL_SHIFT_RIGHT,
XOR,
LOOKUPSWITCH,
MONITORENTER,
MONITOREXIT,
NEW,
NEWARRAY,
MULTIANEWARRAY,
PUT,
SWAP,
TABLESWITCH,
WIDE;
static Optional<Opcode> get(String text) {
for (Opcode opcode : Opcode.values()) {
if (opcode.toString().equals(text)) {
return Optional.of(opcode);
}
}
return Optional.empty();
}
}

View file

@ -6,7 +6,7 @@ import nl.sander.beejava.constantpool.entry.Utf8Entry;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public abstract class Info<T extends Info> { public abstract class Info<T extends Info<T>> {
protected final Utf8Entry nameEntry; protected final Utf8Entry nameEntry;
protected final Utf8Entry descriptorEntry; protected final Utf8Entry descriptorEntry;

View file

@ -29,10 +29,19 @@ public abstract class ConstantPoolEntry {
return getBytes()[0]; return getBytes()[0];
} }
/**
* The output of this ends up in the class file as the constantpool index of the entry.
*
* @return the cp index of the entry.
*/
public int getIndex() { public int getIndex() {
return index; return index;
} }
/**
* With this the ConstantPoolCreator sets the calculated index.
* @param index the cp index to assign to the entry
*/
public void setIndex(int index) { public void setIndex(int index) {
this.index = index; this.index = index;
} }
@ -61,8 +70,20 @@ public abstract class ConstantPoolEntry {
return (byte) (u16 >>> 8); return (byte) (u16 >>> 8);
} }
protected byte getByte(long bits, int positions) { /**
return (byte) ((bits >>> (positions * 8)) & 0xFF); * get the Nth byte in an "array" of bits encoded in a long (i64). Used to create the stream representation of 64bit numbers
*
* @param bits a long in which a long or a double is encoded.
* @param position the index of the byte (0..7) in the array of bits
* @return the Nth byte in the long
*/
protected byte getByte(long bits, int position) {
if (position > 0 && position < 8) {
return (byte) ((bits >>> (position * 8)) & 0xFF);
} else {
throw new IllegalArgumentException("position must be 0..7");
}
} }
} }

View file

@ -7,6 +7,10 @@ public class FloatEntry extends LeafEntry {
private final float floatVal; private final float floatVal;
public FloatEntry(String floatVal) {
this.floatVal = Float.parseFloat(floatVal);
}
public FloatEntry(float floatVal) { public FloatEntry(float floatVal) {
this.floatVal = floatVal; this.floatVal = floatVal;
} }

View file

@ -7,8 +7,12 @@ public class IntegerEntry extends LeafEntry {
private final int intVal; private final int intVal;
public IntegerEntry(int integer) { public IntegerEntry(String integerVal) {
this.intVal = integer; this.intVal = Integer.parseInt(integerVal);
}
public IntegerEntry(int integerVal) {
this.intVal = integerVal;
} }
@Override @Override

View file

@ -0,0 +1,22 @@
package nl.sander.beejava.operands;
import nl.sander.beejava.Primitive;
public class ConstantOperand extends Operand {
private final Primitive type;
private final String value;
public ConstantOperand(Primitive type, String value) {
this.type = type;
this.value = value;
}
public Primitive getType() {
return type;
}
public String getValue() {
return value;
}
}

View file

@ -0,0 +1,11 @@
package nl.sander.beejava.operands;
public class FieldOperand extends Operand{
public final String className;
public final String fieldName;
public FieldOperand(String className, String fieldName) {
this.className = className;
this.fieldName = fieldName;
}
}

View file

@ -0,0 +1,9 @@
package nl.sander.beejava.operands;
public class LocalVariableOperand extends Operand{
public final String name;
public LocalVariableOperand(String name) {
this.name = name;
}
}

View file

@ -0,0 +1,22 @@
package nl.sander.beejava.operands;
public class MethodOperand extends Operand{
public final String className;
public final String methodName;
public final String signature;
public MethodOperand(String className, String methodName, String signature) {
this.className = className;
this.methodName = methodName;
this.signature = signature;
}
@Override
public String toString() {
return "MethodOperand{" +
"className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", signature='" + signature + '\'' +
'}';
}
}

View file

@ -0,0 +1,4 @@
package nl.sander.beejava.operands;
public abstract class Operand {
}

View file

@ -0,0 +1,9 @@
package nl.sander.beejava.operands;
public class VoidOperand extends Operand{
public final static VoidOperand INSTANCE = new VoidOperand();
private VoidOperand() {
//
}
}

View file

@ -24,6 +24,6 @@ public class BytecodeGeneratorTests {
@Test @Test
public void testFields() throws ClassNotFoundException { public void testFields() throws ClassNotFoundException {
BytecodeGenerator.generate(Compiler.compile(TestData.createClassWithField(int.class))); BytecodeGenerator.generate(Compiler.compile(TestData.createClassWithField(Integer.class)));
} }
} }

View file

@ -1,6 +1,9 @@
package nl.sander.beejava; package nl.sander.beejava;
import nl.sander.beejava.apiv2.*; import nl.sander.beejava.api.BeeField;
import nl.sander.beejava.api.BeeMethod;
import nl.sander.beejava.api.BeeParameter;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.flags.AccessFlags; import nl.sander.beejava.flags.AccessFlags;
import nl.sander.beejava.flags.ClassAccessFlags; import nl.sander.beejava.flags.ClassAccessFlags;
import nl.sander.beejava.flags.FieldAccessFlag; import nl.sander.beejava.flags.FieldAccessFlag;
@ -20,11 +23,16 @@ public class SourceCompilerTest {
BeeSource beeSource = new SourceCompiler(TestData2.simpleBean).doCompile(); BeeSource beeSource = new SourceCompiler(TestData2.simpleBean).doCompile();
assertEquals("com.acme.SimpleBean", beeSource.getName()); assertEquals("com.acme.SimpleBean", beeSource.getName());
assertEquals(ClassAccessFlags.SUPER.getBytecode() | ClassAccessFlags.PUBLIC.getBytecode(), AccessFlags.combine(beeSource.getAccessFlags())); assertEquals(ClassAccessFlags.SUPER.getBytecode() | ClassAccessFlags.PUBLIC.getBytecode(), AccessFlags.combine(beeSource.getAccessFlags()));
assertTrue(beeSource.getFields().contains(new BeeField(Set.of(FieldAccessFlag.PRIVATE), int.class, "value"))); assertTrue(beeSource.getFields().contains(new BeeField(beeSource.getName(), Set.of(FieldAccessFlag.PRIVATE), int.class, "value")));
assertEquals(1, beeSource.getConstructors().size()); assertEquals(1, beeSource.getConstructors().size());
Set<BeeMethod> methods = beeSource.getMethods(); Set<BeeMethod> methods = beeSource.getMethods();
assertEquals(2, methods.size()); assertEquals(2, methods.size());
assertTrue(methods.contains(new BeeMethod("getValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(), int.class, List.of()))); assertTrue(methods.contains(new BeeMethod(beeSource, "getValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(), int.class, List.of())));
assertTrue(methods.contains(new BeeMethod("setValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(new BeeParameter(int.class, "newValue")), Void.TYPE, List.of()))); assertTrue(methods.contains(new BeeMethod(beeSource, "setValue", Set.of(MethodAccessFlag.PUBLIC), Set.of(new BeeParameter(int.class, "newValue")), Void.TYPE, List.of())));
OpcodeTranslator.translate(beeSource);
Compiler.compile(beeSource);
} }
} }

View file

@ -1,113 +1,38 @@
package nl.sander.beejava; package nl.sander.beejava;
import nl.sander.beejava.api.*; import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.flags.FieldAccessFlag;
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.ClassAccessFlags.PUBLIC;
import static nl.sander.beejava.flags.ClassAccessFlags.SUPER;
public class TestData { public class TestData {
public static BeeSource createEmptyClass() throws ClassNotFoundException { public static BeeSource createEmptyClass() {
return BeeSource.builder() return SourceCompiler.compile("""
.withClassFileVersion(Version.V14) class public nl.sander.beejava.test.EmptyBean
.withPackage("nl.sander.beejava.test") constructor public()
.withAccessFlags(PUBLIC, SUPER) INVOKE this.super()
.withSimpleName("EmptyBean") RETURN
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode """);
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
} }
public static BeeSource emptyClassWithInterface() throws ClassNotFoundException { public static BeeSource emptyClassWithInterface() {
return BeeSource.builder() return SourceCompiler.compile("""
.withClassFileVersion(Version.V14) class public nl.sander.beejava.test.EmptyBean implements java.io.Serializable
.withPackage("nl.sander.beejava.test") constructor public()
.withAccessFlags(PUBLIC) INVOKE this.super()V
.withSimpleName("EmptyBean") RETURN
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode """);
.withInterfaces(Serializable.class)
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
} }
public static BeeSource createClassWithTwoReferencesToSomeClass() throws ClassNotFoundException { public static BeeSource createClassWithField(Class<?> fieldType) {
BeeMethod print1 = BeeMethod.builder() String template = """
.withName("print1") class com.acme.SimpleBean(V15)
.withAccessFlags(MethodAccessFlag.PUBLIC) field private %s field
.withCode( constructor public(%s arg)
line(GET, "java.lang.System","out"), INVOKE this.super()V
line(LD_CONST, "1"), LOAD arg
line(INVOKE, "java.io.PrintStream", "println", "java.lang.String"), PUT this.field
line(RETURN)) RETURN
.build(); """;
// INVOKE System.out.println("1") return SourceCompiler.compile(String.format(template, fieldType.getName(), fieldType.getName()));
BeeMethod print2 = BeeMethod.builder()
.withName("print2")
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withCode(
line(GET, "java.lang.System","out"),
line(LD_CONST, "2"),
line(INVOKE, "java.io.PrintStream", "println", "java.lang.String"),
line(RETURN))
.build();
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC, SUPER)
.withSimpleName("ClassWithReferences")
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.withMethods(print1, print2)
.build();
} }
public static BeeSource createClassWithField(Class<?> fieldType) throws ClassNotFoundException {
BeeField field = BeeField.builder()
.withAccessFlags(FieldAccessFlag.PRIVATE)
.withType(fieldType)
.withName("field")
.build();
BeeParameter parameter = BeeParameter.create(fieldType, "value");
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withFormalParameters(parameter)
.withCode(
line(LD_VAR, Ref.THIS),
line(INVOKE, Ref.SUPER, "<init>", "()"),
line(LD_VAR, Ref.THIS),
line(LD_VAR, parameter),
line(PUT, field),
line(RETURN))
.build();
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withSimpleName("Bean")
.withSuperClass(Object.class)
.withFields(field)
.withConstructors(constructor)
.build();
}
public static BeeConstructor createDefaultConstructor() throws ClassNotFoundException {
return BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withCode(
line(LD_VAR, Ref.THIS),
line(INVOKE, Ref.SUPER, "<init>", "()"),
line(RETURN))
.build();
}
} }

View file

@ -3,10 +3,10 @@ package nl.sander.beejava;
public class TestData2 { public class TestData2 {
public final static String simpleBean= """ public final static String simpleBean= """
class com.acme.SimpleBean(V15) class public com.acme.SimpleBean(V15)
field private int value field private int value
constructor public() constructor public()
INVOKE super() INVOKE this.super()
RETURN RETURN
method public getValue() -> int method public getValue() -> int
RETURN this.value RETURN this.value
@ -40,7 +40,7 @@ public class TestData2 {
String constructor_opcope = """ String constructor_opcope = """
constructor nl.sander.beejava.Compiler(nl.sander.beejava.CompiledClass arg_0); constructor nl.sander.beejava.Compiler(nl.sander.beejava.CompiledClass arg_0);
INVOKE super() INVOKE this.super()
NEW nl.sander.beejava.ConstantPoolCreator() NEW nl.sander.beejava.ConstantPoolCreator()
PUT this.constantPoolCreator PUT this.constantPoolCreator

View file

@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.fail;
public class TypeMapperTest { public class TypeMapperTest {
@Test @Test
public void test_int() throws ClassNotFoundException { public void test_int() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(int.class); BeeSource beeSource = TestData.createClassWithField(int.class);
@ -24,7 +24,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_double() throws ClassNotFoundException { public void test_double() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(double.class); BeeSource beeSource = TestData.createClassWithField(double.class);
@ -37,7 +37,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_float() throws ClassNotFoundException { public void test_float() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(float.class); BeeSource beeSource = TestData.createClassWithField(float.class);
@ -50,7 +50,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_byte() throws ClassNotFoundException { public void test_byte() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(byte.class); BeeSource beeSource = TestData.createClassWithField(byte.class);
@ -63,7 +63,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_short() throws ClassNotFoundException { public void test_short() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(short.class); BeeSource beeSource = TestData.createClassWithField(short.class);
@ -76,7 +76,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_long() throws ClassNotFoundException { public void test_long() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(long.class); BeeSource beeSource = TestData.createClassWithField(long.class);
@ -89,7 +89,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_char() throws ClassNotFoundException { public void test_char() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(char.class); BeeSource beeSource = TestData.createClassWithField(char.class);
@ -102,7 +102,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_boolean() throws ClassNotFoundException { public void test_boolean() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(boolean.class); BeeSource beeSource = TestData.createClassWithField(boolean.class);
@ -115,7 +115,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_Object() throws ClassNotFoundException { public void test_Object() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(Object.class); BeeSource beeSource = TestData.createClassWithField(Object.class);
@ -128,7 +128,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_int_array() throws ClassNotFoundException { public void test_int_array() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(int[].class); BeeSource beeSource = TestData.createClassWithField(int[].class);
@ -141,7 +141,7 @@ public class TypeMapperTest {
} }
@Test @Test
public void test_Object_array() throws ClassNotFoundException { public void test_Object_array() {
// Arrange // Arrange
BeeSource beeSource = TestData.createClassWithField(String[].class); BeeSource beeSource = TestData.createClassWithField(String[].class);

View file

@ -12,7 +12,7 @@ public class TagCorrectnessTest {
public void testSpec() { public void testSpec() {
assertEquals(1, utf8().getTag()); assertEquals(1, utf8().getTag());
assertEquals(3, new IntegerEntry(0).getTag()); assertEquals(3, new IntegerEntry(0).getTag());
assertEquals(4, new FloatEntry(0).getTag()); assertEquals(4, new FloatEntry(0F).getTag());
assertEquals(5, new LongEntry(0).getTag()); assertEquals(5, new LongEntry(0).getTag());
assertEquals(6, new DoubleEntry(0).getTag()); assertEquals(6, new DoubleEntry(0).getTag());
assertEquals(7, classEntry().getTag()); assertEquals(7, classEntry().getTag());

View file

@ -1,9 +1,7 @@
package nl.sander.beejava.e2e; package nl.sander.beejava.e2e;
import nl.sander.beejava.BytecodeGenerator;
import nl.sander.beejava.CompiledClass;
import nl.sander.beejava.Compiler; import nl.sander.beejava.Compiler;
import nl.sander.beejava.TestData; import nl.sander.beejava.*;
import nl.sander.beejava.api.BeeSource; import nl.sander.beejava.api.BeeSource;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -13,25 +11,41 @@ import java.lang.reflect.Modifier;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
/** /**
* A rather simple case, still meaningful nonetheless. * Proves ability to compile opcodes for GET, LOAD, INVOKE, RETURN.
* Green means class can be loaded, so the class file structure is valid. * Secondly the usage of method refs, constants.
* The EmptyBean class just contains a default constructor.
*/ */
public class BeanWithMethodsTest { public class BeanWithMethodsTest {
@Test @Test
public void testEmptyBean() throws Exception { public void testEmptyBean() throws Exception {
// Arrange // Arrange
BeeSource emptyClass = TestData.createClassWithTwoReferencesToSomeClass(); BeeSource printer = SourceCompiler.compile("""
class nl.sander.beejava.test.ClassWithReferences
constructor public()
INVOKE this.super()V
RETURN
method public print1()
GET java.lang.System.out
LOAD "1"
INVOKE java/io/PrintStream.println(Ljava/lang/String;)V
RETURN
method public print2()
GET java.lang.System.out
LOAD int 2
INVOKE java/io/PrintStream.println(I)V
RETURN
""");
// Act // Act
CompiledClass compiledClass = Compiler.compile(emptyClass); OpcodeTranslator.translate(printer);
CompiledClass compiledClass = Compiler.compile(printer);
byte[] bytecode = BytecodeGenerator.generate(compiledClass); byte[] bytecode = BytecodeGenerator.generate(compiledClass);
ByteClassLoader classLoader = new ByteClassLoader(); ByteClassLoader classLoader = new ByteClassLoader();
classLoader.setByteCode("nl.sander.beejava.test.ClassWithReferences", bytecode); String className = "nl.sander.beejava.test.ClassWithReferences";
classLoader.setByteCode(className, bytecode);
// Assert // Assert
Class<?> classWithReferences = classLoader.loadClass("nl.sander.beejava.test.ClassWithReferences"); Class<?> classWithReferences = classLoader.loadClass(className);
assertNotNull(classWithReferences); assertNotNull(classWithReferences);
Method[] methods = classWithReferences.getDeclaredMethods(); Method[] methods = classWithReferences.getDeclaredMethods();

View file

@ -1,37 +1,26 @@
package nl.sander.beejava.e2e; package nl.sander.beejava.e2e;
import nl.sander.beejava.BytecodeGenerator; import nl.sander.beejava.OverallCompiler;
import nl.sander.beejava.CompiledClass;
import nl.sander.beejava.Compiler;
import nl.sander.beejava.api.BeeConstructor;
import nl.sander.beejava.api.BeeSource;
import nl.sander.beejava.api.Ref;
import nl.sander.beejava.api.Version;
import nl.sander.beejava.flags.MethodAccessFlag;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import static nl.sander.beejava.api.CodeLine.line;
import static nl.sander.beejava.api.Opcode.*;
import static nl.sander.beejava.flags.ClassAccessFlags.PUBLIC;
import static nl.sander.beejava.flags.ClassAccessFlags.SUPER;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
/** /**
* A rather simple case, still meaningful nonetheless. * Hello world test for bejava. Proves ability to compile bejava-lang to valid bytecode.
* Green means class can be loaded, so the class file structure is valid.
* The EmptyBean class just contains a default constructor.
*/ */
public class EmptyBeanTest { public class EmptyBeanTest {
@Test @Test
public void testEmptyBean() throws Exception { public void testEmptyBean() throws Exception {
// Arrange
BeeSource emptyClass = createEmptyClass();
// Act // Act
CompiledClass compiledClass = Compiler.compile(emptyClass); byte[] bytecode = OverallCompiler.compile("""
byte[] bytecode = BytecodeGenerator.generate(compiledClass); class public nl.sander.beejava.test.EmptyBean
constructor public()
INVOKE this.super()V
RETURN
""");
ByteClassLoader classLoader = new ByteClassLoader(); ByteClassLoader classLoader = new ByteClassLoader();
classLoader.setByteCode("nl.sander.beejava.test.EmptyBean", bytecode); classLoader.setByteCode("nl.sander.beejava.test.EmptyBean", bytecode);
@ -42,27 +31,5 @@ public class EmptyBeanTest {
Object instance = constructor.newInstance(); Object instance = constructor.newInstance();
assertNotNull(instance); assertNotNull(instance);
}
private BeeSource createEmptyClass() throws ClassNotFoundException {
return BeeSource.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC, SUPER)
.withSimpleName("EmptyBean")
.withSuperClass(Object.class) // Not mandatory, like in java sourcecode
.withConstructors(createDefaultConstructor()) // There's no default constructor in beejava. The user must always add them
.build();
}
private BeeConstructor createDefaultConstructor() throws ClassNotFoundException {
return BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withCode(
line(LD_VAR, Ref.THIS),
line(INVOKE, Ref.SUPER, "<init>", "()"),
line(RETURN))
.build();
} }
} }