API draft, constant pool creator working in principle. A working test

This commit is contained in:
Sander Hautvast 2020-11-09 09:56:33 +01:00
parent e9e66f6291
commit 9aad0e55c6
44 changed files with 1545 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
.idea/
target/
.DS_Store
*.iml

View file

@ -0,0 +1,54 @@
Classfile /Users/Shautvast/IdeaProjects/beejava/target/test-classes/nl/sander/beejava/testclasses/IntBean.class
Last modified 2 Oct 2020; size 369 bytes
SHA-256 checksum ef66d886f52e768d038ec9e87724671c6a748ea0cd62b1b117c5cfb31bcdb153
Compiled from "IntBean.java"
public class nl.sander.beejava.testclasses.IntBean
minor version: 0
major version: 58
flags: (0x0021) ACC_PUBLIC, ACC_SUPER
this_class: #8 // nl/sander/beejava/testclasses/IntBean
super_class: #2 // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 1
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Fieldref #8.#9 // nl/sander/beejava/testclasses/IntBean.intField:I
#8 = Class #10 // nl/sander/beejava/testclasses/IntBean
#9 = NameAndType #11:#12 // intField:I
#10 = Utf8 nl/sander/beejava/testclasses/IntBean
#11 = Utf8 intField
#12 = Utf8 I
#13 = Utf8 (I)V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 LocalVariableTable
#17 = Utf8 this
#18 = Utf8 Lnl/sander/beejava/testclasses/IntBean;
#19 = Utf8 SourceFile
#20 = Utf8 IntBean.java
{
public nl.sander.beejava.testclasses.IntBean(int);
descriptor: (I)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #7 // Field intField:I
9: return
LineNumberTable:
line 6: 0
line 7: 4
line 8: 9
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lnl/sander/beejava/testclasses/IntBean;
0 10 1 intField I
}
SourceFile: "IntBean.java"

50
pom.xml Normal file
View file

@ -0,0 +1,50 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>
<name>beejava</name>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>14</source>
<target>14</target>
</configuration>
</plugin>
</plugins>
</build>
<groupId>nl.sander</groupId>
<artifactId>beejava</artifactId>
<version>0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>15</maven.compiler.source>
<maven.compiler.target>15</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>1.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View file

@ -0,0 +1,123 @@
package nl.sander.beejava;
import nl.sander.beejava.flags.ClassAccessFlag;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class BeeClass {
private final Version classFileVersion;
private final BeePackage beePackage;
private final Set<ClassAccessFlag> accessFlags = new HashSet<>();
private final String name;
private final Class<?> superClass;
private final Set<Class<?>> interfaces = new HashSet<>();
private final Set<BeeField> fields = new HashSet<>();
private final Set<BeeConstructor> constructors = new HashSet<>();
private BeeClass(Version classFileVersion,
BeePackage beePackage, Set<ClassAccessFlag> accessFlags, String name, Class<?> superClass,
Set<Class<?>> interfaces, Set<BeeField> fields, Set<BeeConstructor> constructors) {
this.classFileVersion = classFileVersion;
this.beePackage = beePackage;
this.accessFlags.addAll(accessFlags);
this.name = name;
this.superClass = superClass;
this.interfaces.addAll(interfaces);
this.fields.addAll(fields);
this.constructors.addAll(constructors);
}
public static BeeClass.Builder builder() {
return new Builder();
}
public Version getClassFileVersion() {
return classFileVersion;
}
public BeePackage getPackage() {
return beePackage;
}
public String getName() {
return name;
}
public Set<BeeConstructor> getConstructors() {
return constructors;
}
public Set<ClassAccessFlag> getAccessFlags() {
return accessFlags;
}
public Class<?> getSuperClass() {
return superClass;
}
public Set<BeeField> getFields() {
return fields;
}
public static class Builder {
private Version version;
private final Set<ClassAccessFlag> 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 BeePackage beePackage;
private Class<?> superClass = Object.class;
private String name;
private Builder() {
}
public Builder withClassFileVersion(Version version){
this.version=version;
return this;
}
public BeeClass.Builder withPackage(String beePackage) {
this.beePackage = new BeePackage(beePackage);
return this;
}
public BeeClass.Builder withAccessFlags(ClassAccessFlag... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public BeeClass.Builder withName(String name) {
this.name = name;
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 BeeClass build() {
return new BeeClass(version, beePackage, accessFlags, name, superClass, interfaces, fields, constructors);
}
}
}

View file

@ -0,0 +1,85 @@
package nl.sander.beejava;
import nl.sander.beejava.flags.MethodAccessFlag;
import java.util.*;
public class BeeConstructor implements ContainsCode{
private final Set<MethodAccessFlag> accessFlags = new HashSet<>();
private final Set<BeeParameter> formalParameters = new HashSet<>();
private final List<CodeLine> code = new LinkedList<>();
private BeeConstructor(Set<MethodAccessFlag> accessFlags,
List<BeeParameter> formalParameters,
List<CodeLine> code) {
this.formalParameters.addAll(formalParameters);
this.accessFlags.addAll(accessFlags);
this.code.addAll(code);
}
public static Builder builder() {
return new Builder();
}
Set<MethodAccessFlag> getAccessFlags() {
return accessFlags;
}
Set<BeeParameter> getFormalParameters() {
return formalParameters;
}
@Override
public List<CodeLine> getCode() {
return code;
}
@Override
public String toString() {
return "BeeConstructor{" +
"formalParameters=" + formalParameters +
'}';
}
@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);
}
public static class Builder {
private final Set<MethodAccessFlag> accessFlags = new HashSet<>();
private final List<BeeParameter> formalParameters = new LinkedList<>();
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() {
return new BeeConstructor(accessFlags, formalParameters, code);
}
}
}

View file

@ -0,0 +1,79 @@
package nl.sander.beejava;
import nl.sander.beejava.flags.FieldAccessFlag;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class BeeField {
private final Set<FieldAccessFlag> accessFlags = new HashSet<>();
private final Class<?> type;
private final String name;
private BeeField(Set<FieldAccessFlag> accessFlags, Class<?> type, String name) {
this.accessFlags.addAll(accessFlags);
this.type = type;
this.name = name;
}
public static BeeField.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 BeeField.Builder withAccessFlags(FieldAccessFlag... accessFlags) {
this.accessFlags.addAll(Arrays.asList(accessFlags));
return this;
}
public BeeField.Builder withType(Class<?> type) {
this.type=type;
return this;
}
public BeeField.Builder withName(String name) {
this.name=name;
return this;
}
public BeeField build() {
return new BeeField(accessFlags, type, name);
}
}
}

View file

@ -0,0 +1,10 @@
package nl.sander.beejava;
class BeePackage {
private final String name;
BeePackage(String name) {
this.name = name;
}
}

View file

@ -0,0 +1,38 @@
package nl.sander.beejava;
import java.util.Objects;
public class BeeParameter {
private final Class<?> type;
private final String name;
private BeeParameter(Class<?> type, String name) {
this.type = type;
this.name = name;
}
public static BeeParameter create(Class<?> type, String name) {
return new BeeParameter(type, Objects.requireNonNull(name));
}
public Class<?> getType() {
return type;
}
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BeeParameter that = (BeeParameter) o;
return name.equals(that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}

View file

@ -0,0 +1,107 @@
package nl.sander.beejava;
class CodeLine {
private final int linenumber;
private final Opcode opcode;
private Ref ref;
private BeeParameter parameter;
private String methodName;
private String inputSignature;
private String outputSignature;
private BeeField field;
CodeLine(int linenumber, Opcode opcode) {
this.linenumber = linenumber;
this.opcode = opcode;
}
public static CodeLine line(int nr, Opcode opcode, Ref ref) {
return new CodeLine(nr, opcode).withRef(ref);
}
public static CodeLine line(int nr, Opcode opcode, Ref ref, String methodNameRef, String inputSignature) {
return new CodeLine(nr, opcode).withRef(ref).withMethodName(methodNameRef).withInput(inputSignature).withVoidOutput();
}
public static CodeLine line(int nr, Opcode opcode, Ref ref, String methodNameRef, String inputSignature, String outputSignature) {
return new CodeLine(nr, opcode).withRef(ref).withMethodName(methodNameRef).withInput(inputSignature).withOutput(outputSignature);
}
public static CodeLine line(int nr, Opcode opcode) {
return new CodeLine(nr, opcode);
}
public static CodeLine line(int nr, Opcode opcode, BeeParameter parameter) {
return new CodeLine(nr, opcode).withParameter(parameter);
}
public static CodeLine line(int nr, Opcode opcode, BeeField intField) {
return new CodeLine(nr, opcode).withRef(Ref.THIS).withField(intField);
}
private CodeLine withRef(Ref ref) {
this.ref = ref;
return this;
}
private CodeLine withMethodName(String methodName) {
this.methodName = methodName;
return this;
}
private CodeLine withInput(String 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 withField(BeeField field) {
this.field = field;
return this;
}
public String getMethodName() {
return methodName;
}
boolean hasMethod() {
return methodName != null;
}
String getMethodSignature() {
return inputSignature + outputSignature;
}
Ref getRef() {
return ref;
}
boolean hasField() {
return field != null;
}
BeeField getField() {
return field;
}
BeeParameter getParameter() {
return parameter;
}
}

View file

@ -0,0 +1,38 @@
package nl.sander.beejava;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import nl.sander.beejava.util.ByteBuf;
import java.util.Set;
public class Compiler {
private final BeeClass beeClass; // maybe not a member?
private final ConstantTreeCreator constantTreeCreator = new ConstantTreeCreator();
private final ConstantPoolCreator constantPoolCreator = new ConstantPoolCreator();
public Compiler(BeeClass beeClass) {
this.beeClass = beeClass;
}
public static byte[] compile(BeeClass beeClass) {
Compiler compiler = new Compiler(beeClass);
return compiler.doCompile();
}
private byte[] doCompile() {
ByteBuf buf = new ByteBuf();
buf.add(0xCA, 0xFE, 0xBA, 0xBE);
buf.add(beeClass.getClassFileVersion().getMinor());
buf.add(beeClass.getClassFileVersion().getMajor());
Set<ConstantPoolEntry> constantTree = constantTreeCreator.createConstantTree(beeClass);
ConstantPool constantPool = constantPoolCreator.createConstantPool(constantTree);
buf.add(constantPool.getBytes());
return buf.toBytes();
}
}

View file

@ -0,0 +1,47 @@
package nl.sander.beejava;
import nl.sander.beejava.constantpool.ConstantPool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import java.util.Set;
/**
* Transforms the hierachical constant tree into a flat datastructure. The walks the tree adding indexes to each element.
*/
public class ConstantPoolCreator {
private ConstantPool constantPool;
private int index;
public ConstantPool createConstantPool(Set<ConstantPoolEntry> constantTree) {
constantPool = new ConstantPool();
constantPool.add(null); // dummy element to align it's index with the indexes in the elements themselves
index = 0;
updateToplevelElements(constantTree);
return constantPool;
}
private void updateToplevelElements(Set<ConstantPoolEntry> children) {
for (ConstantPoolEntry child : children) {
addToPool(child);
updateChildElements(child.getChildren());
// first the complete toplevel element including it's children, then next toplevel element
}
}
private void updateChildElements(Set<ConstantPoolEntry> children) {
// first all direct children
for (ConstantPoolEntry child : children) {
addToPool(child);
}
// then further lineage
for (ConstantPoolEntry child : children) {
updateChildElements(child.getChildren());
}
}
private void addToPool(ConstantPoolEntry entry) {
index += 1;
entry.setIndex(index);
constantPool.add(entry);
}
}

View file

@ -0,0 +1,78 @@
package nl.sander.beejava;
import nl.sander.beejava.constantpool.entry.*;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Builds a set of a tree of constant pool entries that refer to each other.
*
* A client must supply a {@link BeeClass} containing a set of {@link CodeLine}s that is assumed to be correct.
* It doesn't check if a valid state is reached.
*/
/* So the name isn't entirely correct. Waiting for inspiration.
* also TODO make sure entries aren't duplicates
*/
public class ConstantTreeCreator {
private final Set<ConstantPoolEntry> constantTree = new LinkedHashSet<>();
private BeeClass beeClass;
public Set<ConstantPoolEntry> createConstantTree(BeeClass beeClass) {
constantTree.clear();
this.beeClass = beeClass;
beeClass.getConstructors().forEach(this::updateConstantTree);
// TODO update constantTree for fields ?
// TODO update constantTree for methods
return constantTree;
}
private void updateConstantTree(ContainsCode codeContainer) {
codeContainer.getCode().forEach(this::updateConstantTree);
}
// TODO construct multi root tree
private void updateConstantTree(CodeLine codeline) {
if (codeline.hasMethod()){
addMethodRef(codeline);
}
if (codeline.hasField()) {
addFieldRef(codeline);
}
}
private void addMethodRef(CodeLine codeline) {
constantTree.add(new MethodRefEntry(createClassName(codeline), createMethodNameAndType(codeline)));
}
private void addFieldRef(CodeLine codeline) {
constantTree.add(new FieldRefEntry(createClassName(codeline), createFieldNameAndType(codeline)));
}
private NameAndTypeEntry createMethodNameAndType(CodeLine codeline) {
return new NameAndTypeEntry(new Utf8Entry(codeline.getMethodName()), new Utf8Entry(codeline.getMethodSignature()));
}
private NameAndTypeEntry createFieldNameAndType(CodeLine codeline) {
return new NameAndTypeEntry(new Utf8Entry(codeline.getField().getName()), new Utf8Entry(TypeMapper.map(codeline.getField().getType())));
}
private ClassEntry createClassName(CodeLine codeline) {
return new ClassEntry(new Utf8Entry(internalName(getNameOfClass(codeline))));
}
private String getNameOfClass(CodeLine codeline) {
if (codeline.getRef() == Ref.SUPER) {
return beeClass.getSuperClass().getName();
} else if (codeline.getRef() == Ref.THIS) {
return beeClass.getPackage() + "." + beeClass.getName();
}
throw new RuntimeException("shouldn't be here");
}
private String internalName(String name) {
return name.replaceAll("\\.", "/");
}
}

View file

@ -0,0 +1,8 @@
package nl.sander.beejava;
import java.util.List;
public interface ContainsCode {
List<CodeLine> getCode();
}

View file

@ -0,0 +1,69 @@
package nl.sander.beejava;
public enum Opcode {
LOAD("load"),
STORE("store"),
CONST("const"),
NEWARRAY("new"),
RETURN("return"),
ARRAYLENGTH("length"),
THROW("throw"),
PUSH("push"),
CHECKCAST("checkcast"),
ADD("add"),
COMPARE("cmp"),
DIVIDE("div"),
MULTIPLY("mul"),
NEGATE("neg"),
REMAINDER("rem"),
SUBTRACT("sub"),
DUPLICATE("dup"),
DUPLICATE_DOWN("dup_x1"),
DUPLICATE2("dup2"),
DUPLICATE2_DOWN("dup2_x1"),
GET("get"),
GOTO("goto"),
GOTO_W("goto_w"),
TO_DOUBLE("2d"),
TO_INT("2i"),
TO_LONG("2l"),
TO_BYTE("2b"),
TO_CHAR("2c"),
TO_FLOAT("2f"),
TO_SHORT("2s"),
AND("and"),
IF_EQUAL("ifeq"),
IF_NOT_EQUAL("ifneq"),
IF_LESS_THAN("iflt"),
IF_GREATER_OR_EQUAL("ifge"),
IF_GREATER_THAN("ifgt"),
IF_LESS_OR_EQUAL("ifle"),
IF_NOT_NULL("ifnotnull"),
IF_NULL("ifnull"),
INCREMENT("inc"),
INSTANCEOF("instanceof"),
INVOKE("invoke"),
OR("or"),
SHIFT_LEFT("shr"),
SHIFT_RIGHT("shl"),
LOGICAL_SHIFT_RIGHT("ushr"),
XOR("xor"),
LOOKUPSWITCH("lookupswitch"),
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;
Opcode(String name) {
this.name=name;
}
}

View file

@ -0,0 +1,22 @@
package nl.sander.beejava;
/**
* Only used to contain void return
*/
public final class Output {
public static final Output VOID =new Output();
private Output() {
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean equals(Object obj) {
return getClass() == obj.getClass();
}
}

View file

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

View file

@ -0,0 +1,20 @@
package nl.sander.beejava;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
public class TypeMapper {
private static final Map<Class<?>, String> MAP = new ConcurrentHashMap<>();
static {
MAP.put(int.class, "I");
}
public static String map(Class<?> type) {
return Optional.ofNullable(MAP.get(type))
.orElseThrow(() -> new RuntimeException("Type " + type.getName() + " not found")); //this MUST not happen -> TODO map all types
}
}

View file

@ -0,0 +1,34 @@
package nl.sander.beejava;
public enum Version {
V1_0_2(45),
V1_1(45),
V1_2(46),
V1_3(47),
V1_4(48),
V5_0(49),
V6(50),
V7(51),
V8(52),
V9(53),
V10(54),
V11(55),
V12(56),
V13(57),
V14(58),
V15(59);
private final int major;
Version(int major) {
this.major = major;
}
public int getMajor() {
return major;
}
public int getMinor(){
return 0;
}
}

View file

@ -0,0 +1,31 @@
package nl.sander.beejava.constantpool;
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
import java.util.ArrayList;
import java.util.List;
public class ConstantPool {
private final List<ConstantPoolEntry> entries=new ArrayList<>();
public int getIndex(ConstantPoolEntry entry){
for (int i=0; i<entries.size(); i++){
if (entries.get(i)==entry){
return i+1;
}
}
return -1;
}
public void add(int index, ConstantPoolEntry entry){
entries.add(index, entry);
}
public void add(ConstantPoolEntry entry){
entries.add(entry);
}
public byte[] getBytes() {
return new byte[]{};
}
}

View file

@ -0,0 +1,21 @@
package nl.sander.beejava.constantpool.entry;
public class ClassEntry extends ConstantPoolEntry {
private final Utf8Entry name;
public ClassEntry(Utf8Entry name) {
super(name);
this.name = name;
}
public int getNameIndex() {
return name.getIndex();
}
@Override
public String toString() {
return "ClassEntry{" +
"nameIndex=" + getNameIndex() +
'}';
}
}

View file

@ -0,0 +1,27 @@
package nl.sander.beejava.constantpool.entry;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
public abstract class ConstantPoolEntry {
protected final Set<ConstantPoolEntry> children;
private int index;
protected ConstantPoolEntry(ConstantPoolEntry... children) {
this.children = new LinkedHashSet<>();
this.children.addAll(Arrays.asList(children)); // java8 way destroys order, not desastrous, but I like to preserve it.
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public Set<ConstantPoolEntry> getChildren() {
return children;
}
}

View file

@ -0,0 +1,10 @@
package nl.sander.beejava.constantpool.entry;
public class DoubleEntry extends LeafEntry {
private final double doubleVal;
public DoubleEntry(double doubleVal) {
this.doubleVal = doubleVal;
}
}

View file

@ -0,0 +1,28 @@
package nl.sander.beejava.constantpool.entry;
public class FieldRefEntry extends ConstantPoolEntry {
private final ClassEntry classEntry;
private final NameAndTypeEntry nameAndTypeEntry;
public FieldRefEntry(ClassEntry classEntry, NameAndTypeEntry nameAndTypeEntry) {
super(classEntry, nameAndTypeEntry);
this.classEntry = classEntry;
this.nameAndTypeEntry = nameAndTypeEntry;
}
public int getClassIndex() {
return classEntry.getIndex();
}
public int getNameAndTypeIndex() {
return nameAndTypeEntry.getIndex();
}
@Override
public String toString() {
return "FieldRefEntry{" +
"classIndex=" + getClassIndex() +
", nameAndTypeIndex=" + getNameAndTypeIndex() +
'}';
}
}

View file

@ -0,0 +1,16 @@
package nl.sander.beejava.constantpool.entry;
public class FloatEntry extends LeafEntry {
private final float floatVal;
public FloatEntry(float floatVal) {
this.floatVal = floatVal;
}
@Override
public String toString() {
return "FloatEntry{" +
"floatVal=" + floatVal +
'}';
}
}

View file

@ -0,0 +1,16 @@
package nl.sander.beejava.constantpool.entry;
public class IntEntry extends LeafEntry {
private final int intVal;
public IntEntry(int integer) {
this.intVal = integer;
}
@Override
public String toString() {
return "IntEntry{" +
"intVal=" + intVal +
'}';
}
}

View file

@ -0,0 +1,20 @@
package nl.sander.beejava.constantpool.entry;
public class InterfaceMethodRefEntry extends ConstantPoolEntry {
private final ClassEntry classEntry;
private final NameAndTypeEntry nameAndTypeEntry;
public InterfaceMethodRefEntry(ClassEntry classEntry, NameAndTypeEntry nameAndTypeEntry) {
super(classEntry,nameAndTypeEntry);
this.classEntry = classEntry;
this.nameAndTypeEntry = nameAndTypeEntry;
}
public int getClassIndex(){
return classEntry.getIndex();
}
public int getNameAndTypeIndex(){
return nameAndTypeEntry.getIndex();
}
}

View file

@ -0,0 +1,17 @@
//package nl.sander.beejava.constantpool.entry;
//
//public class InvokeDynamicEntry extends ConstantPoolEntry {
// private final int bootstrapMethodAttrIndex; //??
// private final NameAndTypeEntry nameAndTypeEntry;
//
//
//
// @Override
// public String toString() {
// return "InvokeDynamicEntry{" +
// "bootstrapMethodAttrIndex=" + bootstrapMethodAttrIndex +
// ", nameAndTypeIndex=" + nameAndTypeIndex +
// '}';
// }
//}
//TODO implement later

View file

@ -0,0 +1,11 @@
package nl.sander.beejava.constantpool.entry;
import java.util.Collections;
import java.util.Set;
public class LeafEntry extends ConstantPoolEntry {
@Override
public Set<ConstantPoolEntry> getChildren() {
return Collections.emptySet();
}
}

View file

@ -0,0 +1,17 @@
package nl.sander.beejava.constantpool.entry;
public class LongEntry extends LeafEntry {
private final long longVal;
public LongEntry(long longVal) {
this.longVal = longVal;
}
@Override
public String toString() {
return "LongEntry{" +
"longVal=" + longVal +
'}';
}
}

View file

@ -0,0 +1,21 @@
//package nl.sander.beejava.constantpool.entry;
//
//public class MethodHandleEntry extends ConstantPoolEntry {
// private final int referenceKind;
// private final int referenceIndex;
//
// public MethodHandleEntry(int referenceKind, int referenceIndex) {
// this.referenceKind = referenceKind;
// this.referenceIndex = referenceIndex;
// }
//
// @Override
// public String toString() {
// return "MethodHandleEntry{" +
// "referenceKind=" + referenceKind +
// ", referenceIndex=" + referenceIndex +
// '}';
// }
//}
//TODO implement later

View file

@ -0,0 +1,21 @@
package nl.sander.beejava.constantpool.entry;
public class MethodRefEntry extends ConstantPoolEntry {
private final ClassEntry classEntry;
private final NameAndTypeEntry nameAndTypeEntry;
public MethodRefEntry(ClassEntry classEntry, NameAndTypeEntry nameAndTypeEntry) {
super(classEntry,nameAndTypeEntry);
this.classEntry = classEntry;
this.nameAndTypeEntry = nameAndTypeEntry;
}
public int getClassIndex() {
return classEntry.getIndex();
}
public int getNameAndTypeIndex() {
return nameAndTypeEntry.getIndex();
}
}

View file

@ -0,0 +1,18 @@
//package nl.sander.beejava.constantpool.entry;
//
//public class MethodTypeEntry extends ConstantPoolEntry {
// private final int descriptorIndex;
//
// public MethodTypeEntry(int descriptorIndex) {
// this.descriptorIndex = descriptorIndex;
// }
//
// @Override
// public String toString() {
// return "MethodTypeEntry{" +
// "descriptorIndex=" + descriptorIndex +
// '}';
// }
//}
//TODO implement when I know that this is

View file

@ -0,0 +1,11 @@
package nl.sander.beejava.constantpool.entry;
public class ModuleEntry extends ConstantPoolEntry {
private final Utf8Entry nameEntry;
public ModuleEntry(Utf8Entry nameEntry) {
super(nameEntry);
this.nameEntry = nameEntry;
}
}

View file

@ -0,0 +1,29 @@
package nl.sander.beejava.constantpool.entry;
public class NameAndTypeEntry extends ConstantPoolEntry {
private final Utf8Entry name;
private final Utf8Entry type;
public NameAndTypeEntry(Utf8Entry name, Utf8Entry type) {
super(name,type);
this.name = name;
this.type = type;
}
public int getNameIndex() {
return name.getIndex();
}
public int getTypeIndex() {
return type.getIndex();
}
@Override
public String toString() {
return "NameAndTypeEntry{" +
"nameIndex=" + getNameIndex() +
", typeIndex=" + getTypeIndex() +
'}';
}
}

View file

@ -0,0 +1,14 @@
package nl.sander.beejava.constantpool.entry;
public class PackageEntry extends ConstantPoolEntry {
private final Utf8Entry name;
public PackageEntry(Utf8Entry name) {
super(name);
this.name = name;
}
public int getNameIndex() {
return name.getIndex();
}
}

View file

@ -0,0 +1,20 @@
package nl.sander.beejava.constantpool.entry;
public class StringEntry extends ConstantPoolEntry {
private final Utf8Entry utf8;
public StringEntry(Utf8Entry utf8) {
this.utf8 = utf8;
}
public int getUtf8Index() {
return utf8.getIndex();
}
@Override
public String toString() {
return "StringEntry{" +
"utf8Index=" + getUtf8Index() +
'}';
}
}

View file

@ -0,0 +1,20 @@
package nl.sander.beejava.constantpool.entry;
public class Utf8Entry extends LeafEntry {
private final String stringVal;
public Utf8Entry(String utf8) {
this.stringVal = utf8;
}
public String getUtf8() {
return stringVal;
}
@Override
public String toString() {
return "Utf8Entry{" +
"stringVal='" + stringVal + '\'' +
'}';
}
}

View file

@ -0,0 +1,12 @@
package nl.sander.beejava.flags;
import java.util.Set;
public interface AccessFlag {
int getBytecode();
default int getCombineBytecode(Set<AccessFlag> accessflags) {
return accessflags.stream().mapToInt(AccessFlag::getBytecode).sum();
}
}

View file

@ -0,0 +1,26 @@
package nl.sander.beejava.flags;
import nl.sander.beejava.flags.AccessFlag;
public enum ClassAccessFlag implements AccessFlag {
PUBLIC(0x0001), // Declared public; may be accessed from outside its package.
FINAL(0x0010), // Declared final; no subclasses allowed.
SUPER(0x0020), // Treat superclass methods specially when invoked by the invokespecial instruction.
INTERFACE(0x0200), // Is an interface, not a class.
ABSTRACT(0x0400), // Declared abstract; must not be instantiated.
SYNTHETIC(0x1000), // Declared synthetic; not present in the source code.
ANNOTATION(0x2000), // Declared as an annotation type.
ENUM(0x4000), // Declared as an enum type.
MODULE(0x8000); // Is a module, not a class or interface.
private final int bytecode;
ClassAccessFlag(int bytecode) {
this.bytecode=bytecode;
}
@Override
public int getBytecode() {
return bytecode;
}
}

View file

@ -0,0 +1,25 @@
package nl.sander.beejava.flags;
import nl.sander.beejava.flags.AccessFlag;
public enum FieldAccessFlag implements AccessFlag {
PUBLIC(0x0001), // Declared public; may be accessed from outside itspackage.
PRIVATE(0x0002), // Declared private; accessible only within the defining class and other classes belonging to the samenest (§5.4.4).
PROTECTED(0x0004), // Declared protected; may be accessed within subclasses.
STATIC(0x0008), // Declared static.
FINAL(0x0010), // Declared final; never directly assigned to afterobject construction (JLS §17.5).
VOLATILE(0x0040), // Declared volatile; cannot be cached.
ACC_TRANSIENT(0x0080), // Declared transient; not written or read by apersistent object manager.
SYNTHETIC(0x1000), // Declared synthetic; not present in the source code.
ENUM(0x4000); //Declared as an element of an enum.
private final int bytecode;
FieldAccessFlag(int bytecode) {
this.bytecode = bytecode;
}
public int getBytecode() {
return bytecode;
}
}

View file

@ -0,0 +1,28 @@
package nl.sander.beejava.flags;
import nl.sander.beejava.flags.AccessFlag;
public enum MethodAccessFlag implements AccessFlag {
PUBLIC(0x0001), // Declared public; may be accessed from outside its package.
PRIVATE(0x0002), // Declared private; accessible only within the defining class and other classes belonging to the same nest (§5.4.4).
PROTECTED(0x0004), // Declared protected; may be accessed within subclasses.
STATIC(0x0008), // Declared static.
FINAL(0x0010), // Declared final; must not be overridden (§5.4.5).
SYNCHRONIZED(0x0020), // Declared synchronized; invocation is wrapped by a monitor use.
BRIDGE(0x0040), // A bridge method, generated by the compiler.
VARARGS(0x0080), //Declared with variable number of arguments.
NATIVE(0x0100), //Declared native; implemented in a language other than the Java programming language.
ABSTRACT(0x0400), // Declared abstract; no implementation is provided.
STRICT(0x0800), // Declared strictfp; floating-point mode is FP-strict.
SYNTHETIC(0x1000); // Declared synthetic; not present in the source code.
private final int bytecode;
MethodAccessFlag(int bytecode) {
this.bytecode = bytecode;
}
public int getBytecode() {
return bytecode;
}
}

View file

@ -0,0 +1,86 @@
package nl.sander.beejava.util;
import java.nio.ByteBuffer;
import java.nio.charset.*;
/**
* storage like ArrayList, with bytebuffer instead of array
*/
public class ByteBuf {
private ByteBuffer data;
public ByteBuf() {
this(64);
}
public ByteBuf(final int initialSize) {
data = ByteBuffer.allocate(initialSize);
}
public String toString() {
return toString(StandardCharsets.UTF_8);
}
public String toString(Charset charset) {
data.flip();
CharsetDecoder decoder = charset.newDecoder(); // decode is not threadsafe, might put it in threadlocal
// but I don't think this (newDecoder()+config) is expensive
decoder.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
try {
return decoder.decode(data).toString();
} catch (CharacterCodingException e) {
throw new RuntimeException(e);
}
}
public void clear() {
data.clear();
}
public void add(final byte c) {
if (data.remaining() == 0) {
enlarge(1);
}
data.put(c);
}
public void add(final byte[] bytes) {
if (data.remaining() < bytes.length) {
enlarge(bytes.length);
}
data.put(bytes);
}
public void add(final int... ints) {
if (data.remaining() < ints.length) {
enlarge(ints.length);
}
byte[] bytes = new byte[ints.length];
for (int i = 0; i < ints.length; i++) {
bytes[i] = (byte) (i & 0xFF);
}
add(bytes);
}
private void enlarge(final int size) {
final int length1 = 2 * data.limit();
final int length2 = data.limit() + size;
ByteBuffer newData = ByteBuffer.allocate(Math.max(length1, length2));
data.flip();
newData.put(data);
data = newData;
}
public int length() {
return data.limit() - data.remaining();
}
public byte[] toBytes() {
return data.array();
}
}

View file

@ -0,0 +1,114 @@
package nl.sander.beejava;
import nl.sander.beejava.constantpool.entry.*;
import nl.sander.beejava.flags.FieldAccessFlag;
import nl.sander.beejava.flags.MethodAccessFlag;
import org.junit.jupiter.api.Test;
import java.util.Iterator;
import java.util.Set;
import static nl.sander.beejava.CodeLine.line;
import static nl.sander.beejava.Opcode.*;
import static nl.sander.beejava.flags.ClassAccessFlag.PUBLIC;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ConstantTeeTests {
@Test
public void testMethodRefEntryForSuperConstructor() {
BeeClass classWithIntField = createEmptyClass();
Set<ConstantPoolEntry> constantTree = new ConstantTreeCreator().createConstantTree(classWithIntField);
assertEquals(1, constantTree.size());
ConstantPoolEntry superConstructor = constantTree.iterator().next();
assertEquals(MethodRefEntry.class, superConstructor.getClass());
MethodRefEntry methodRefEntry = (MethodRefEntry) superConstructor;
Set<ConstantPoolEntry> methodRefEntryChildren = methodRefEntry.getChildren();
assertEquals(2, methodRefEntryChildren.size());
Iterator<ConstantPoolEntry> firstChildren = methodRefEntryChildren.iterator();
ConstantPoolEntry child1 = firstChildren.next();
assertEquals(ClassEntry.class, child1.getClass());
ClassEntry classEntry = (ClassEntry) child1;
Set<ConstantPoolEntry> classEntryChildren = classEntry.getChildren();
assertEquals(1, classEntryChildren.size());
ConstantPoolEntry child2 = classEntryChildren.iterator().next();
assertEquals(Utf8Entry.class, child2.getClass());
Utf8Entry className = (Utf8Entry) child2;
assertEquals("java/lang/Object", className.getUtf8());
ConstantPoolEntry child3 = firstChildren.next();
assertEquals(NameAndTypeEntry.class, child3.getClass());
NameAndTypeEntry nameAndTypeEntry = (NameAndTypeEntry) child3;
Set<ConstantPoolEntry> nameAndTypeEntryChildren = nameAndTypeEntry.getChildren();
assertEquals(2, nameAndTypeEntryChildren.size());
Iterator<ConstantPoolEntry> nameAndTypeChildrenIterator = nameAndTypeEntryChildren.iterator();
ConstantPoolEntry child4 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child4.getClass());
Utf8Entry name = (Utf8Entry) child4;
assertEquals("<init>", name.getUtf8());
ConstantPoolEntry child5 = nameAndTypeChildrenIterator.next();
assertEquals(Utf8Entry.class, child5.getClass());
Utf8Entry type = (Utf8Entry) child5;
assertEquals("()V", type.getUtf8());
}
private BeeClass createEmptyClass() {
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withCode(
line(0, LOAD, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(5, RETURN))
.build();
return BeeClass.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withName("EmptyBean")
.withSuperClass(Object.class)
.withConstructors(constructor)
.build();
}
private BeeClass createClassWithIntField() {
BeeField intField = BeeField.builder()
.withAccessFlags(FieldAccessFlag.PRIVATE)
.withType(int.class)
.withName("intField")
.build();
BeeParameter intValueParameter = BeeParameter.create(int.class, "intValue");
BeeConstructor constructor = BeeConstructor.builder()
.withAccessFlags(MethodAccessFlag.PUBLIC)
.withFormalParameters(intValueParameter)
.withCode(
line(0, LOAD, Ref.THIS),
line(1, INVOKE, Ref.SUPER, "<init>", "()"),
line(2, LOAD, Ref.THIS),
line(3, LOAD, intValueParameter),
line(4, PUT, intField),
line(5, RETURN))
.build();
return BeeClass.builder()
.withClassFileVersion(Version.V14)
.withPackage("nl.sander.beejava.test")
.withAccessFlags(PUBLIC)
.withName("IntBean")
.withSuperClass(Object.class)
.withFields(intField)
.withConstructors(constructor)
.build();
}
}

View file

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