first commit i a long while. Should have committed earlier, but tests were failing.
* updated bejava lang * bug fixes
This commit is contained in:
parent
5e7267c170
commit
22a6930973
54 changed files with 966 additions and 1506 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
8
pom.xml
8
pom.xml
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
51
src/main/java/nl/sander/beejava/FieldWrapper.java
Normal file
51
src/main/java/nl/sander/beejava/FieldWrapper.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/main/java/nl/sander/beejava/JavaInstruction.java
Normal file
81
src/main/java/nl/sander/beejava/JavaInstruction.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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),
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
214
src/main/java/nl/sander/beejava/OpcodeTranslator.java
Normal file
214
src/main/java/nl/sander/beejava/OpcodeTranslator.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/main/java/nl/sander/beejava/OverallCompiler.java
Normal file
17
src/main/java/nl/sander/beejava/OverallCompiler.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/main/java/nl/sander/beejava/Primitive.java
Normal file
14
src/main/java/nl/sander/beejava/Primitive.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,42 +85,41 @@ 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++));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (methodName==null){
|
if (methodName == null) {
|
||||||
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) {
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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)){
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,17 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Float\t\t" +floatVal;
|
return "Float\t\t" + floatVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/main/java/nl/sander/beejava/operands/FieldOperand.java
Normal file
11
src/main/java/nl/sander/beejava/operands/FieldOperand.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
22
src/main/java/nl/sander/beejava/operands/MethodOperand.java
Normal file
22
src/main/java/nl/sander/beejava/operands/MethodOperand.java
Normal 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 + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/main/java/nl/sander/beejava/operands/Operand.java
Normal file
4
src/main/java/nl/sander/beejava/operands/Operand.java
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
package nl.sander.beejava.operands;
|
||||||
|
|
||||||
|
public abstract class Operand {
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package nl.sander.beejava.operands;
|
||||||
|
|
||||||
|
public class VoidOperand extends Operand{
|
||||||
|
public final static VoidOperand INSTANCE = new VoidOperand();
|
||||||
|
|
||||||
|
private VoidOperand() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -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,36 +11,52 @@ 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();
|
||||||
assertEquals(2, methods.length);
|
assertEquals(2, methods.length);
|
||||||
assertEquals("print1", methods[0].getName());
|
assertEquals("print1", methods[0].getName());
|
||||||
assertTrue(Modifier.isPublic(methods[0].getModifiers()));
|
assertTrue(Modifier.isPublic(methods[0].getModifiers()));
|
||||||
assertEquals(0,methods[0].getParameterCount());
|
assertEquals(0, methods[0].getParameterCount());
|
||||||
|
|
||||||
assertEquals("print2", methods[1].getName()); // ordering may cause failures
|
assertEquals("print2", methods[1].getName()); // ordering may cause failures
|
||||||
assertTrue(Modifier.isPublic(methods[1].getModifiers()));
|
assertTrue(Modifier.isPublic(methods[1].getModifiers()));
|
||||||
assertEquals(0,methods[1].getParameterCount());
|
assertEquals(0, methods[1].getParameterCount());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue