package generators.loadtime;
import java.lang.classfile.*;
import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
import java.lang.classfile.attribute.StackMapFrameInfo;
import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.instruction.*;
import java.lang.constant.*;
import java.util.*;
import static java.lang.constant.ConstantDescs.*;
public class FrameTracker {
private static final int ITEM_TOP = 0,
ITEM_INTEGER = 1,
ITEM_FLOAT = 2,
ITEM_DOUBLE = 3,
ITEM_LONG = 4,
ITEM_NULL = 5,
ITEM_UNINITIALIZED_THIS = 6,
ITEM_OBJECT = 7,
ITEM_UNINITIALIZED = 8,
ITEM_BOOLEAN = 9,
ITEM_BYTE = 10,
ITEM_SHORT = 11,
ITEM_CHAR = 12,
ITEM_LONG_2ND = 13,
ITEM_DOUBLE_2ND = 14;
public static ArrayList frames(StateMachineBuilder smb, CodeModel src_com) {
var ft = new FrameTracker(smb, src_com);
var frames = new ArrayList();
for(var coe : src_com){
if(coe instanceof Instruction) {
frames.add(new Frame(ft.locals(), ft.stack(), ft.bci, ft.current_line_number, ft.local_annotations()));
System.out.println(frames.getLast());
}
ft.encounter(coe);
}
frames.add(new Frame(ft.locals(), ft.stack(), ft.bci, null, ft.local_annotations()));
return frames;
}
private LocalVariableAnnotation[] local_annotations() {
return this.activeAnnotations.toArray(LocalVariableAnnotation[]::new);
}
public Type[] locals() {
return locals.toArray(Type[]::new);
}
public Type[] stack() {
return stack.toArray(Type[]::new);
}
public record Type(int tag, ClassDesc sym, int bci) {
static final Type TOP_TYPE = simpleType(ITEM_TOP),
NULL_TYPE = simpleType(ITEM_NULL),
INTEGER_TYPE = simpleType(ITEM_INTEGER),
FLOAT_TYPE = simpleType(ITEM_FLOAT),
LONG_TYPE = simpleType(ITEM_LONG),
LONG2_TYPE = simpleType(ITEM_LONG_2ND),
DOUBLE_TYPE = simpleType(ITEM_DOUBLE),
BOOLEAN_TYPE = simpleType(ITEM_BOOLEAN),
BYTE_TYPE = simpleType(ITEM_BYTE),
CHAR_TYPE = simpleType(ITEM_CHAR),
SHORT_TYPE = simpleType(ITEM_SHORT),
DOUBLE2_TYPE = simpleType(ITEM_DOUBLE_2ND),
UNITIALIZED_THIS_TYPE = simpleType(ITEM_UNINITIALIZED_THIS);
static final Type STRING_TYPE = referenceType(CD_String);
static final Type METHOD_HANDLE_TYPE = referenceType(CD_MethodHandle);
static final Type METHOD_TYPE = referenceType(CD_MethodType);
@Override
public String toString(){
return switch(tag){
case ITEM_TOP -> "TOP";
case ITEM_INTEGER -> "int";
case ITEM_FLOAT -> "float";
case ITEM_DOUBLE -> "double";
case ITEM_LONG -> "long";
case ITEM_NULL -> sym==null?"null":"null("+sym.displayName()+")";
case ITEM_UNINITIALIZED_THIS -> sym==null?"uninitialized(this)":"uninitialized(this "+sym.displayName()+")";
case ITEM_OBJECT -> "Object("+sym.displayName()+")";
case ITEM_UNINITIALIZED -> sym==null?"uninitialized()":"uninitialized("+sym.displayName()+")";
case ITEM_BOOLEAN -> "boolean";
case ITEM_BYTE -> "byte";
case ITEM_SHORT -> "short";
case ITEM_CHAR -> "char";
case ITEM_LONG_2ND -> "long2";
case ITEM_DOUBLE_2ND -> "double2";
default -> throw new IllegalStateException("Unexpected value: " + tag);
};
}
private static Type simpleType(int tag) {
return new Type(tag, null, 0);
}
static Type referenceType(ClassDesc desc) {
return new Type(ITEM_OBJECT, desc, 0);
}
static Type uninitializedType(ClassDesc sym, int bci) {
return new Type(ITEM_UNINITIALIZED, sym, bci);
}
@Override
public boolean equals(Object o) {
return (o instanceof Type(int tag, ClassDesc sym, int bci))
&& tag == this.tag && bci == this.bci && Objects.equals(this.sym, sym);
}
boolean isCategory2_2nd() {
return this == DOUBLE2_TYPE || this == LONG2_TYPE;
}
boolean isReference() {
return tag == ITEM_OBJECT || this == NULL_TYPE;
}
boolean isObject() {
return tag == ITEM_OBJECT && sym.isClassOrInterface();
}
boolean isArray() {
return tag == ITEM_OBJECT && sym.isArray();
}
static Type verificationType(StackMapFrameInfo.VerificationTypeInfo v, FrameTracker t){
return switch (v){
case StackMapFrameInfo.ObjectVerificationTypeInfo o ->
Type.referenceType(o.classSymbol());
case StackMapFrameInfo.SimpleVerificationTypeInfo s ->
Type.simpleType(s.tag());
case StackMapFrameInfo.UninitializedVerificationTypeInfo u ->
Type.uninitializedType(null, t.bciMap.get(u.newTarget()));
};
}
ClassDesc toCD(){
return switch (tag) {
case ITEM_BOOLEAN -> CD_boolean;
case ITEM_BYTE -> CD_byte;
case ITEM_CHAR -> CD_char;
case ITEM_SHORT -> CD_short;
case ITEM_INTEGER -> CD_int;
case ITEM_LONG -> CD_long;
case ITEM_FLOAT -> CD_float;
case ITEM_DOUBLE -> CD_double;
case ITEM_OBJECT -> sym;
case ITEM_NULL -> CD_Object;
default -> throw new RuntimeException();
};
}
}
final ArrayList stack = new ArrayList<>();
final ArrayList locals = new ArrayList<>();
final StateMachineBuilder smb;
LineNumber current_line_number = null;
int bci = 0;
HashMap