renamed constants and added bytecode tags
This commit is contained in:
parent
61ca5f9a11
commit
7968d27814
36 changed files with 442 additions and 279 deletions
|
|
@ -2,7 +2,7 @@ package nl.sander.beejava;
|
||||||
|
|
||||||
import nl.sander.beejava.api.BeeClass;
|
import nl.sander.beejava.api.BeeClass;
|
||||||
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.NodeConstant;
|
||||||
import nl.sander.beejava.util.ByteBuf;
|
import nl.sander.beejava.util.ByteBuf;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -28,7 +28,7 @@ public class Compiler {
|
||||||
buf.add(beeClass.getClassFileVersion().getMinor());
|
buf.add(beeClass.getClassFileVersion().getMinor());
|
||||||
buf.add(beeClass.getClassFileVersion().getMajor());
|
buf.add(beeClass.getClassFileVersion().getMajor());
|
||||||
|
|
||||||
Set<ConstantPoolEntry> constantTree = constantTreeCreator.createConstantTree(beeClass);
|
Set<NodeConstant> constantTree = constantTreeCreator.createConstantTree(beeClass);
|
||||||
ConstantPool constantPool = constantPoolCreator.createConstantPool(constantTree);
|
ConstantPool constantPool = constantPoolCreator.createConstantPool(constantTree);
|
||||||
|
|
||||||
buf.add(constantPool.getBytes());
|
buf.add(constantPool.getBytes());
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package nl.sander.beejava;
|
package nl.sander.beejava;
|
||||||
|
|
||||||
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.NodeConstant;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
@ -9,10 +9,11 @@ import java.util.Set;
|
||||||
* Transforms the hierachical constant tree into a flat datastructure. The walks the tree adding indexes to each element.
|
* Transforms the hierachical constant tree into a flat datastructure. The walks the tree adding indexes to each element.
|
||||||
*/
|
*/
|
||||||
public class ConstantPoolCreator {
|
public class ConstantPoolCreator {
|
||||||
private ConstantPool constantPool;
|
private ConstantPool constantPool; // the constant pool that is being created
|
||||||
private int index;
|
private int index; // the current index that will be assigned to a constant pool entry. It needs to be unique for each entry.
|
||||||
|
// References to other elements in the pool are made through indexes, so they have to be valid to guarantee that the class can be loaded by the JVM.
|
||||||
|
|
||||||
public ConstantPool createConstantPool(Set<ConstantPoolEntry> constantTree) {
|
public ConstantPool createConstantPool(Set<NodeConstant> constantTree) {
|
||||||
constantPool = new ConstantPool();
|
constantPool = new ConstantPool();
|
||||||
constantPool.add(null); // dummy element to align it's index with the indexes in the elements themselves
|
constantPool.add(null); // dummy element to align it's index with the indexes in the elements themselves
|
||||||
index = 0;
|
index = 0;
|
||||||
|
|
@ -20,26 +21,26 @@ public class ConstantPoolCreator {
|
||||||
return constantPool;
|
return constantPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateToplevelElements(Set<ConstantPoolEntry> children) {
|
private void updateToplevelElements(Set<NodeConstant> children) {
|
||||||
for (ConstantPoolEntry child : children) {
|
for (NodeConstant child : children) {
|
||||||
addToPool(child);
|
addToPool(child);
|
||||||
updateChildElements(child.getChildren());
|
updateChildElements(child.getChildren());
|
||||||
// first the complete toplevel element including it's children, then next toplevel element
|
// first the complete toplevel element including it's children, then next toplevel element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateChildElements(Set<ConstantPoolEntry> children) {
|
private void updateChildElements(Set<NodeConstant> children) {
|
||||||
// first all direct children
|
// first all direct children
|
||||||
for (ConstantPoolEntry child : children) {
|
for (NodeConstant child : children) {
|
||||||
addToPool(child);
|
addToPool(child);
|
||||||
}
|
}
|
||||||
// then further lineage
|
// then further lineage
|
||||||
for (ConstantPoolEntry child : children) {
|
for (NodeConstant child : children) {
|
||||||
updateChildElements(child.getChildren());
|
updateChildElements(child.getChildren());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addToPool(ConstantPoolEntry entry) {
|
private void addToPool(NodeConstant entry) {
|
||||||
index += 1;
|
index += 1;
|
||||||
entry.setIndex(index);
|
entry.setIndex(index);
|
||||||
constantPool.add(entry);
|
constantPool.add(entry);
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ import java.util.Set;
|
||||||
* also TODO make sure entries aren't duplicates
|
* also TODO make sure entries aren't duplicates
|
||||||
*/
|
*/
|
||||||
public class ConstantTreeCreator {
|
public class ConstantTreeCreator {
|
||||||
private final Set<ConstantPoolEntry> constantTree = new LinkedHashSet<>();
|
private final Set<NodeConstant> constantTree = new LinkedHashSet<>();
|
||||||
private BeeClass beeClass;
|
private BeeClass beeClass;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -28,7 +28,7 @@ public class ConstantTreeCreator {
|
||||||
* @param beeClass the Class object for which the constant pool needs to be created
|
* @param beeClass the Class object for which the constant pool needs to be created
|
||||||
* @return a Set of constant pool entries
|
* @return a Set of constant pool entries
|
||||||
*/
|
*/
|
||||||
public Set<ConstantPoolEntry> createConstantTree(BeeClass beeClass) {
|
public Set<NodeConstant> createConstantTree(BeeClass beeClass) {
|
||||||
constantTree.clear();
|
constantTree.clear();
|
||||||
this.beeClass = beeClass;
|
this.beeClass = beeClass;
|
||||||
beeClass.getConstructors().forEach(this::updateConstantTree);
|
beeClass.getConstructors().forEach(this::updateConstantTree);
|
||||||
|
|
@ -55,23 +55,23 @@ public class ConstantTreeCreator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMethod(CodeLine codeline) {
|
private void addMethod(CodeLine codeline) {
|
||||||
constantTree.add(new MethodRefEntry(createClassName(codeline), createMethodNameAndType(codeline)));
|
constantTree.add(new ConstantMethodRef(createClassName(codeline), createMethodNameAndType(codeline)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addField(CodeLine codeline) {
|
private void addField(CodeLine codeline) {
|
||||||
constantTree.add(new FieldRefEntry(createClassName(codeline), createFieldNameAndType(codeline)));
|
constantTree.add(new ConstantFieldRef(createClassName(codeline), createFieldNameAndType(codeline)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private NameAndTypeEntry createMethodNameAndType(CodeLine codeline) {
|
private ConstantNameAndType createMethodNameAndType(CodeLine codeline) {
|
||||||
return new NameAndTypeEntry(new Utf8Entry(codeline.getMethodName()), new Utf8Entry(codeline.getMethodSignature()));
|
return new ConstantNameAndType(new ConstantUtf8(codeline.getMethodName()), new ConstantUtf8(codeline.getMethodSignature()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private NameAndTypeEntry createFieldNameAndType(CodeLine codeline) {
|
private ConstantNameAndType createFieldNameAndType(CodeLine codeline) {
|
||||||
return new NameAndTypeEntry(new Utf8Entry(codeline.getField().getName()), new Utf8Entry(TypeMapper.map(codeline.getField().getType())));
|
return new ConstantNameAndType(new ConstantUtf8(codeline.getField().getName()), new ConstantUtf8(TypeMapper.map(codeline.getField().getType())));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClassEntry createClassName(CodeLine codeline) {
|
private ConstantClass createClassName(CodeLine codeline) {
|
||||||
return new ClassEntry(new Utf8Entry(internalName(getNameOfClass(codeline))));
|
return new ConstantClass(new ConstantUtf8(internalName(getNameOfClass(codeline))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getNameOfClass(CodeLine codeline) {
|
private String getNameOfClass(CodeLine codeline) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
package nl.sander.beejava.constantpool;
|
package nl.sander.beejava.constantpool;
|
||||||
|
|
||||||
import nl.sander.beejava.constantpool.entry.ConstantPoolEntry;
|
import nl.sander.beejava.constantpool.entry.NodeConstant;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class ConstantPool {
|
public class ConstantPool {
|
||||||
private final List<ConstantPoolEntry> entries=new ArrayList<>();
|
private final List<NodeConstant> entries=new ArrayList<>();
|
||||||
|
|
||||||
public int getIndex(ConstantPoolEntry entry){
|
public int getIndex(NodeConstant entry){
|
||||||
for (int i=0; i<entries.size(); i++){
|
for (int i=0; i<entries.size(); i++){
|
||||||
if (entries.get(i)==entry){
|
if (entries.get(i)==entry){
|
||||||
return i+1;
|
return i+1;
|
||||||
|
|
@ -17,11 +17,7 @@ public class ConstantPool {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(int index, ConstantPoolEntry entry){
|
public void add(NodeConstant entry){
|
||||||
entries.add(index, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(ConstantPoolEntry entry){
|
|
||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
public class ClassEntry extends ConstantPoolEntry {
|
public class ConstantClass extends NodeConstant {
|
||||||
private final Utf8Entry name;
|
private final ConstantUtf8 name;
|
||||||
|
|
||||||
public ClassEntry(Utf8Entry name) {
|
public ConstantClass(ConstantUtf8 name) {
|
||||||
super(name);
|
super(name);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
@ -18,4 +18,9 @@ public class ClassEntry extends ConstantPoolEntry {
|
||||||
"nameIndex=" + getNameIndex() +
|
"nameIndex=" + getNameIndex() +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantDouble extends LeafConstant {
|
||||||
|
private final double doubleVal;
|
||||||
|
|
||||||
|
public ConstantDouble(double doubleVal) {
|
||||||
|
this.doubleVal = doubleVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantDynamic extends NodeConstant {
|
||||||
|
private final int bootstrapMethodIndex; // TODO
|
||||||
|
private final ConstantNameAndType nameAndType;
|
||||||
|
|
||||||
|
public ConstantDynamic(int bootstrapMethodIndex, ConstantNameAndType nameAndType) {
|
||||||
|
this.bootstrapMethodIndex = bootstrapMethodIndex;
|
||||||
|
this.nameAndType = nameAndType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 17;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameAndTypeIndex(){
|
||||||
|
return nameAndType.getIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantFieldRef extends NodeConstant {
|
||||||
|
private final ConstantClass constantClass;
|
||||||
|
private final ConstantNameAndType constantNameAndType;
|
||||||
|
|
||||||
|
public ConstantFieldRef(ConstantClass constantClass, ConstantNameAndType constantNameAndType) {
|
||||||
|
super(constantClass, constantNameAndType);
|
||||||
|
this.constantClass = constantClass;
|
||||||
|
this.constantNameAndType = constantNameAndType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getClassIndex() {
|
||||||
|
return constantClass.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameAndTypeIndex() {
|
||||||
|
return constantNameAndType.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FieldRefEntry{" +
|
||||||
|
"classIndex=" + getClassIndex() +
|
||||||
|
", nameAndTypeIndex=" + getNameAndTypeIndex() +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
public class FloatEntry extends LeafEntry {
|
public class ConstantFloat extends LeafConstant {
|
||||||
private final float floatVal;
|
private final float floatVal;
|
||||||
|
|
||||||
public FloatEntry(float floatVal) {
|
public ConstantFloat(float floatVal) {
|
||||||
this.floatVal = floatVal;
|
this.floatVal = floatVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,4 +13,9 @@ public class FloatEntry extends LeafEntry {
|
||||||
"floatVal=" + floatVal +
|
"floatVal=" + floatVal +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
public class IntEntry extends LeafEntry {
|
public class ConstantInteger extends LeafConstant {
|
||||||
private final int intVal;
|
private final int intVal;
|
||||||
|
|
||||||
public IntEntry(int integer) {
|
public ConstantInteger(int integer) {
|
||||||
this.intVal = integer;
|
this.intVal = integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -13,4 +13,9 @@ public class IntEntry extends LeafEntry {
|
||||||
"intVal=" + intVal +
|
"intVal=" + intVal +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantInterfaceMethodRef extends NodeConstant {
|
||||||
|
private final ConstantClass constantClass;
|
||||||
|
private final ConstantNameAndType constantNameAndType;
|
||||||
|
|
||||||
|
public ConstantInterfaceMethodRef(ConstantClass constantClass, ConstantNameAndType constantNameAndType) {
|
||||||
|
super(constantClass, constantNameAndType);
|
||||||
|
this.constantClass = constantClass;
|
||||||
|
this.constantNameAndType = constantNameAndType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getClassIndex(){
|
||||||
|
return constantClass.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameAndTypeIndex(){
|
||||||
|
return constantNameAndType.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantInvokeDynamic extends NodeConstant {
|
||||||
|
private final int bootstrapMethodAttrIndex; //??
|
||||||
|
private final ConstantNameAndType constantNameAndType;
|
||||||
|
|
||||||
|
public ConstantInvokeDynamic(int bootstrapMethodAttrIndex, ConstantNameAndType constantNameAndType) {
|
||||||
|
this.bootstrapMethodAttrIndex = bootstrapMethodAttrIndex;
|
||||||
|
this.constantNameAndType = constantNameAndType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "InvokeDynamicEntry{" +
|
||||||
|
"bootstrapMethodAttrIndex=" + bootstrapMethodAttrIndex +
|
||||||
|
", nameAndTypeIndex=" + constantNameAndType.getIndex() +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//TODO implement later
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
public class LongEntry extends LeafEntry {
|
public class ConstantLong extends LeafConstant {
|
||||||
|
|
||||||
private final long longVal;
|
private final long longVal;
|
||||||
|
|
||||||
public LongEntry(long longVal) {
|
public ConstantLong(long longVal) {
|
||||||
this.longVal = longVal;
|
this.longVal = longVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,4 +14,9 @@ public class LongEntry extends LeafEntry {
|
||||||
"longVal=" + longVal +
|
"longVal=" + longVal +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
//TODO implement later
|
||||||
|
public class ConstantMethodHandle extends NodeConstant {
|
||||||
|
private final int referenceKind;
|
||||||
|
|
||||||
|
// only 1 of these can be present:
|
||||||
|
private ConstantFieldRef constantFieldRef;
|
||||||
|
private ConstantMethodRef constantMethodRef;
|
||||||
|
private ConstantInterfaceMethodRef constantInterfaceMethodRef;
|
||||||
|
|
||||||
|
public ConstantMethodHandle(int referenceKind) {
|
||||||
|
this.referenceKind = referenceKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MethodHandleEntry{" +
|
||||||
|
"referenceKind=" + referenceKind +
|
||||||
|
", referenceIndex=" + getReferenceIndex() +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getReferenceIndex() {
|
||||||
|
return 0; //TODO implement
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantMethodRef extends NodeConstant {
|
||||||
|
private final ConstantClass constantClass;
|
||||||
|
private final ConstantNameAndType constantNameAndType;
|
||||||
|
|
||||||
|
public ConstantMethodRef(ConstantClass constantClass, ConstantNameAndType constantNameAndType) {
|
||||||
|
super(constantClass, constantNameAndType);
|
||||||
|
this.constantClass = constantClass;
|
||||||
|
this.constantNameAndType = constantNameAndType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getClassIndex() {
|
||||||
|
return constantClass.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameAndTypeIndex() {
|
||||||
|
return constantNameAndType.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantMethodType extends NodeConstant {
|
||||||
|
private final ConstantUtf8 methodDescriptor;
|
||||||
|
|
||||||
|
public ConstantMethodType(ConstantUtf8 methodDescriptor) {
|
||||||
|
this.methodDescriptor = methodDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MethodTypeEntry{" +
|
||||||
|
"methodDescriptor=" + methodDescriptor +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantModule extends NodeConstant {
|
||||||
|
|
||||||
|
private final ConstantUtf8 nameEntry;
|
||||||
|
|
||||||
|
public ConstantModule(ConstantUtf8 nameEntry) {
|
||||||
|
super(nameEntry);
|
||||||
|
this.nameEntry = nameEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 19;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
public class NameAndTypeEntry extends ConstantPoolEntry {
|
public class ConstantNameAndType extends NodeConstant {
|
||||||
private final Utf8Entry name;
|
private final ConstantUtf8 name;
|
||||||
private final Utf8Entry type;
|
private final ConstantUtf8 type;
|
||||||
|
|
||||||
public NameAndTypeEntry(Utf8Entry name, Utf8Entry type) {
|
public ConstantNameAndType(ConstantUtf8 name, ConstantUtf8 type) {
|
||||||
super(name,type);
|
super(name,type);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
@ -26,4 +26,9 @@ public class NameAndTypeEntry extends ConstantPoolEntry {
|
||||||
", typeIndex=" + getTypeIndex() +
|
", typeIndex=" + getTypeIndex() +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantPackage extends NodeConstant {
|
||||||
|
private final ConstantUtf8 name;
|
||||||
|
|
||||||
|
public ConstantPackage(ConstantUtf8 name) {
|
||||||
|
super(name);
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNameIndex() {
|
||||||
|
return name.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
public class StringEntry extends ConstantPoolEntry {
|
public class ConstantString extends NodeConstant {
|
||||||
private final Utf8Entry utf8;
|
private final ConstantUtf8 utf8;
|
||||||
|
|
||||||
public StringEntry(Utf8Entry utf8) {
|
public ConstantString(ConstantUtf8 utf8) {
|
||||||
this.utf8 = utf8;
|
this.utf8 = utf8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -17,4 +17,9 @@ public class StringEntry extends ConstantPoolEntry {
|
||||||
"utf8Index=" + getUtf8Index() +
|
"utf8Index=" + getUtf8Index() +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
public class ConstantUtf8 extends LeafConstant {
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public ConstantUtf8(String utf8) {
|
||||||
|
this.value = utf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUtf8() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Utf8Entry{" +
|
||||||
|
"value='" + value + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getTag() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
|
||||||
|
|
||||||
public class DoubleEntry extends LeafEntry {
|
|
||||||
private final double doubleVal;
|
|
||||||
|
|
||||||
public DoubleEntry(double doubleVal) {
|
|
||||||
this.doubleVal = doubleVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
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() +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
//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
|
|
||||||
|
|
@ -3,9 +3,12 @@ package nl.sander.beejava.constantpool.entry;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class LeafEntry extends ConstantPoolEntry {
|
/**
|
||||||
|
* Is a constant without children
|
||||||
|
*/
|
||||||
|
public abstract class LeafConstant extends NodeConstant {
|
||||||
@Override
|
@Override
|
||||||
public Set<ConstantPoolEntry> getChildren() {
|
public Set<NodeConstant> getChildren() {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
//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
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
//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
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
package nl.sander.beejava.constantpool.entry;
|
|
||||||
|
|
||||||
public class ModuleEntry extends ConstantPoolEntry {
|
|
||||||
|
|
||||||
private final Utf8Entry nameEntry;
|
|
||||||
|
|
||||||
public ModuleEntry(Utf8Entry nameEntry) {
|
|
||||||
super(nameEntry);
|
|
||||||
this.nameEntry = nameEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public abstract class NodeConstant {
|
||||||
|
protected final Set<NodeConstant> children; // TODO decide whether or not to keep this. Could also make getChildren abstract and make it return a new list every time
|
||||||
|
// TODO to save storage. getChildren should only be called once, so it is probably more efficient like that
|
||||||
|
private int index; // the index of the entry is not known until after creation of the complete tree, so cannot be final, but it should not be updated.
|
||||||
|
|
||||||
|
protected NodeConstant(NodeConstant... children) {
|
||||||
|
this.children = new LinkedHashSet<>();
|
||||||
|
this.children.addAll(Arrays.asList(children)); // java8 way destroys order, not desastrous, but I like to preserve it.
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The tag that indicates the entry type in the bytecode.
|
||||||
|
* §4.4 Table 4.4-A. Constant pool tags
|
||||||
|
*/
|
||||||
|
public abstract int getTag();
|
||||||
|
|
||||||
|
public int getIndex() {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIndex(int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<NodeConstant> getChildren() {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
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 + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,45 +20,45 @@ public class ConstantTreeCreatorTests {
|
||||||
@Test // This is not a maintainable test
|
@Test // This is not a maintainable test
|
||||||
public void testMethodRefEntryForSuperConstructor() {
|
public void testMethodRefEntryForSuperConstructor() {
|
||||||
BeeClass classWithIntField = createEmptyClass();
|
BeeClass classWithIntField = createEmptyClass();
|
||||||
Set<ConstantPoolEntry> constantTree = new ConstantTreeCreator().createConstantTree(classWithIntField);
|
Set<NodeConstant> constantTree = new ConstantTreeCreator().createConstantTree(classWithIntField);
|
||||||
assertEquals(1, constantTree.size());
|
assertEquals(1, constantTree.size());
|
||||||
ConstantPoolEntry superConstructor = constantTree.iterator().next();
|
NodeConstant superConstructor = constantTree.iterator().next();
|
||||||
|
|
||||||
assertEquals(MethodRefEntry.class, superConstructor.getClass());
|
assertEquals(ConstantMethodRef.class, superConstructor.getClass());
|
||||||
MethodRefEntry methodRefEntry = (MethodRefEntry) superConstructor;
|
ConstantMethodRef constantMethodRef = (ConstantMethodRef) superConstructor;
|
||||||
|
|
||||||
Set<ConstantPoolEntry> methodRefEntryChildren = methodRefEntry.getChildren();
|
Set<NodeConstant> methodRefEntryChildren = constantMethodRef.getChildren();
|
||||||
assertEquals(2, methodRefEntryChildren.size());
|
assertEquals(2, methodRefEntryChildren.size());
|
||||||
|
|
||||||
Iterator<ConstantPoolEntry> firstChildren = methodRefEntryChildren.iterator();
|
Iterator<NodeConstant> firstChildren = methodRefEntryChildren.iterator();
|
||||||
ConstantPoolEntry child1 = firstChildren.next();
|
NodeConstant child1 = firstChildren.next();
|
||||||
assertEquals(ClassEntry.class, child1.getClass());
|
assertEquals(ConstantClass.class, child1.getClass());
|
||||||
ClassEntry classEntry = (ClassEntry) child1;
|
ConstantClass constantClass = (ConstantClass) child1;
|
||||||
|
|
||||||
Set<ConstantPoolEntry> classEntryChildren = classEntry.getChildren();
|
Set<NodeConstant> classEntryChildren = constantClass.getChildren();
|
||||||
assertEquals(1, classEntryChildren.size());
|
assertEquals(1, classEntryChildren.size());
|
||||||
ConstantPoolEntry child2 = classEntryChildren.iterator().next();
|
NodeConstant child2 = classEntryChildren.iterator().next();
|
||||||
|
|
||||||
assertEquals(Utf8Entry.class, child2.getClass());
|
assertEquals(ConstantUtf8.class, child2.getClass());
|
||||||
Utf8Entry className = (Utf8Entry) child2;
|
ConstantUtf8 className = (ConstantUtf8) child2;
|
||||||
assertEquals("java/lang/Object", className.getUtf8());
|
assertEquals("java/lang/Object", className.getUtf8());
|
||||||
|
|
||||||
ConstantPoolEntry child3 = firstChildren.next();
|
NodeConstant child3 = firstChildren.next();
|
||||||
assertEquals(NameAndTypeEntry.class, child3.getClass());
|
assertEquals(ConstantNameAndType.class, child3.getClass());
|
||||||
NameAndTypeEntry nameAndTypeEntry = (NameAndTypeEntry) child3;
|
ConstantNameAndType constantNameAndType = (ConstantNameAndType) child3;
|
||||||
|
|
||||||
Set<ConstantPoolEntry> nameAndTypeEntryChildren = nameAndTypeEntry.getChildren();
|
Set<NodeConstant> nameAndTypeEntryChildren = constantNameAndType.getChildren();
|
||||||
assertEquals(2, nameAndTypeEntryChildren.size());
|
assertEquals(2, nameAndTypeEntryChildren.size());
|
||||||
Iterator<ConstantPoolEntry> nameAndTypeChildrenIterator = nameAndTypeEntryChildren.iterator();
|
Iterator<NodeConstant> nameAndTypeChildrenIterator = nameAndTypeEntryChildren.iterator();
|
||||||
|
|
||||||
ConstantPoolEntry child4 = nameAndTypeChildrenIterator.next();
|
NodeConstant child4 = nameAndTypeChildrenIterator.next();
|
||||||
assertEquals(Utf8Entry.class, child4.getClass());
|
assertEquals(ConstantUtf8.class, child4.getClass());
|
||||||
Utf8Entry name = (Utf8Entry) child4;
|
ConstantUtf8 name = (ConstantUtf8) child4;
|
||||||
assertEquals("<init>", name.getUtf8());
|
assertEquals("<init>", name.getUtf8());
|
||||||
|
|
||||||
ConstantPoolEntry child5 = nameAndTypeChildrenIterator.next();
|
NodeConstant child5 = nameAndTypeChildrenIterator.next();
|
||||||
assertEquals(Utf8Entry.class, child5.getClass());
|
assertEquals(ConstantUtf8.class, child5.getClass());
|
||||||
Utf8Entry type = (Utf8Entry) child5;
|
ConstantUtf8 type = (ConstantUtf8) child5;
|
||||||
assertEquals("()V", type.getUtf8());
|
assertEquals("()V", type.getUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
package nl.sander.beejava.constantpool.entry;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make sure tags were put in the code according to spec (§4.4)
|
||||||
|
*/
|
||||||
|
public class TagCorrectnessTest {
|
||||||
|
@Test
|
||||||
|
public void testSpec() {
|
||||||
|
assertEquals(7, classEntry().getTag());
|
||||||
|
assertEquals(9, fieldRef().getTag());
|
||||||
|
assertEquals(10, new ConstantMethodRef(classEntry(), nameAndType()).getTag());
|
||||||
|
assertEquals(11, new ConstantInterfaceMethodRef(classEntry(), nameAndType()).getTag());
|
||||||
|
assertEquals(8, new ConstantString(utf8()).getTag());
|
||||||
|
assertEquals(3, new ConstantInteger(0).getTag());
|
||||||
|
assertEquals(4, new ConstantFloat(0).getTag());
|
||||||
|
assertEquals(5, new ConstantLong(0).getTag());
|
||||||
|
assertEquals(6, new ConstantDouble(0).getTag());
|
||||||
|
assertEquals(12, nameAndType().getTag());
|
||||||
|
assertEquals(1, utf8().getTag());
|
||||||
|
assertEquals(15, new ConstantMethodHandle(0).getTag()); //TODO
|
||||||
|
assertEquals(16, new ConstantMethodType(utf8()).getTag()); //TODO
|
||||||
|
assertEquals(17, new ConstantDynamic(0, nameAndType()).getTag()); //TODO
|
||||||
|
assertEquals(18, new ConstantInvokeDynamic(0, nameAndType()).getTag()); //TODO
|
||||||
|
assertEquals(19, new ConstantModule(utf8()).getTag());
|
||||||
|
assertEquals(20, new ConstantPackage(utf8()).getTag());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConstantFieldRef fieldRef() {
|
||||||
|
return new ConstantFieldRef(classEntry(), nameAndType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConstantNameAndType nameAndType() {
|
||||||
|
return new ConstantNameAndType(utf8(), utf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ConstantClass classEntry() {
|
||||||
|
return new ConstantClass(utf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ConstantUtf8 utf8() {
|
||||||
|
return new ConstantUtf8("");
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue