From 3a2c5290f3908cf7a7dee74d1c52dcc0d6b40d24 Mon Sep 17 00:00:00 2001 From: Parker TenBroeck <51721964+ParkerTenBroeck@users.noreply.github.com> Date: Sat, 3 May 2025 12:11:52 -0400 Subject: [PATCH] adding semantics to allow for cancelation behavior of local variables --- src/AsyncExamples.java | 20 +- src/generators/loadtime/Frame.java | 5 +- src/generators/loadtime/FrameTracker.java | 222 ++++++++++-------- .../loadtime/StateMachineBuilder.java | 17 +- 4 files changed, 155 insertions(+), 109 deletions(-) diff --git a/src/AsyncExamples.java b/src/AsyncExamples.java index c605037..1227121 100644 --- a/src/AsyncExamples.java +++ b/src/AsyncExamples.java @@ -7,6 +7,10 @@ import future.Future; import future.Waker; import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -70,7 +74,7 @@ public class AsyncExamples { public static Future echo(Socket socket){ try(socket){ - var buffer = ByteBuffer.allocate(4096*16*3); + @Test var buffer = ByteBuffer.allocate(4096*16*3); while(true){ var read = socket.read(buffer).await(); buffer.clear().limit(read); @@ -84,7 +88,7 @@ public class AsyncExamples { public static Future echoForever(String message){ byte[] msg_bytes = message.getBytes(StandardCharsets.UTF_8); - try(var socket = Socket.connect(new InetSocketAddress("localhost", 42069)).await()){ + try(@Test var socket = Socket.connect(new InetSocketAddress("localhost", 42069)).await()){ var buffer = ByteBuffer.allocate(message.length()); while(true){ buffer.limit(message.length()).put(msg_bytes).position(0); @@ -98,8 +102,20 @@ public class AsyncExamples { buffer.clear(); } } catch (Exception e) { + e.printStackTrace(); } return Future.ret(null); } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE}) + @interface Test { + + } + + static class Meow implements AutoCloseable{ + @Override + public void close() throws Exception { + } + } } diff --git a/src/generators/loadtime/Frame.java b/src/generators/loadtime/Frame.java index 73e7f29..807d591 100644 --- a/src/generators/loadtime/Frame.java +++ b/src/generators/loadtime/Frame.java @@ -1,13 +1,14 @@ package generators.loadtime; import java.lang.classfile.CodeBuilder; +import java.lang.classfile.instruction.LineNumber; import java.util.Arrays; -public record Frame(FrameTracker.Type[] locals, FrameTracker.Type[] stack) { +public record Frame(FrameTracker.Type[] locals, FrameTracker.Type[] stack, int bci, LineNumber line) { @Override public String toString() { - return "Frame[label =" + Arrays.toString(locals) + ", s = " + Arrays.toString(stack) + "]"; + return "Frame[l=" + Arrays.toString(locals) + ", s=" + Arrays.toString(stack) + ", bci=" + bci + ", line="+line + "]"; } public void save_locals(StateMachineBuilder smb, CodeBuilder cob, SavedStateTracker sst, int loc_off){ diff --git a/src/generators/loadtime/FrameTracker.java b/src/generators/loadtime/FrameTracker.java index f2c428f..beb486b 100644 --- a/src/generators/loadtime/FrameTracker.java +++ b/src/generators/loadtime/FrameTracker.java @@ -1,5 +1,6 @@ package generators.loadtime; + import java.lang.classfile.*; import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute; @@ -8,6 +9,7 @@ import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.instruction.*; import java.lang.constant.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Objects; @@ -31,6 +33,21 @@ public class FrameTracker { 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)); + System.out.println(frames.getLast() + " " + coe); + } + ft.encounter(coe); + } + frames.add(new Frame(ft.locals(), ft.stack(), ft.bci, null)); + + return frames; + } + public Type[] locals() { return locals.toArray(Type[]::new); } @@ -55,21 +72,9 @@ public class FrameTracker { DOUBLE2_TYPE = simpleType(ITEM_DOUBLE_2ND), UNITIALIZED_THIS_TYPE = simpleType(ITEM_UNINITIALIZED_THIS); - //frequently used types to reduce footprint - static final Type OBJECT_TYPE = referenceType(CD_Object), - THROWABLE_TYPE = referenceType(CD_Throwable), - INT_ARRAY_TYPE = referenceType(CD_int.arrayType()), - BOOLEAN_ARRAY_TYPE = referenceType(CD_boolean.arrayType()), - BYTE_ARRAY_TYPE = referenceType(CD_byte.arrayType()), - CHAR_ARRAY_TYPE = referenceType(CD_char.arrayType()), - SHORT_ARRAY_TYPE = referenceType(CD_short.arrayType()), - LONG_ARRAY_TYPE = referenceType(CD_long.arrayType()), - DOUBLE_ARRAY_TYPE = referenceType(CD_double.arrayType()), - FLOAT_ARRAY_TYPE = referenceType(CD_float.arrayType()), - STRING_TYPE = referenceType(CD_String), - CLASS_TYPE = referenceType(CD_Class), - METHOD_HANDLE_TYPE = referenceType(CD_MethodHandle), - METHOD_TYPE = referenceType(CD_MethodType); + 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(){ @@ -101,13 +106,14 @@ public class FrameTracker { return new Type(ITEM_OBJECT, desc, 0); } - static Type uninitializedType(ClassDesc sym) { - return new Type(ITEM_UNINITIALIZED, sym, 0); + static Type uninitializedType(ClassDesc sym, int bci) { + return new Type(ITEM_UNINITIALIZED, sym, bci); } - @Override //mandatory override to avoid use of method reference during JDK bootstrap + @Override public boolean equals(Object o) { - return (o instanceof Type t) && t.tag == tag && t.bci == bci && Objects.equals(sym, t.sym); + return (o instanceof Type(int tag, ClassDesc sym, int bci)) + && tag == this.tag && bci == this.bci && Objects.equals(this.sym, sym); } boolean isCategory2_2nd() { @@ -132,7 +138,7 @@ public class FrameTracker { case StackMapFrameInfo.ObjectVerificationTypeInfo o -> Type.referenceType(o.classSymbol()); case StackMapFrameInfo.SimpleVerificationTypeInfo s -> Type.simpleType(s.tag()); case StackMapFrameInfo.UninitializedVerificationTypeInfo u -> { - //Type.simpleType(u.tag()); +// Type.uninitializedType(null, u.newTarget()); throw new RuntimeException(); } }; @@ -153,59 +159,23 @@ public class FrameTracker { default -> throw new RuntimeException(); }; } - - Type toArray() { - return switch (tag) { - case ITEM_BOOLEAN -> BOOLEAN_ARRAY_TYPE; - case ITEM_BYTE -> BYTE_ARRAY_TYPE; - case ITEM_CHAR -> CHAR_ARRAY_TYPE; - case ITEM_SHORT -> SHORT_ARRAY_TYPE; - case ITEM_INTEGER -> INT_ARRAY_TYPE; - case ITEM_LONG -> LONG_ARRAY_TYPE; - case ITEM_FLOAT -> FLOAT_ARRAY_TYPE; - case ITEM_DOUBLE -> DOUBLE_ARRAY_TYPE; - case ITEM_OBJECT -> Type.referenceType(sym.arrayType()); - default -> OBJECT_TYPE; - }; - } - - Type getComponent() { - if (isArray()) { - var comp = sym.componentType(); - if (comp.isPrimitive()) { - return switch (comp.descriptorString().charAt(0)) { - case 'Z' -> Type.BOOLEAN_TYPE; - case 'B' -> Type.BYTE_TYPE; - case 'C' -> Type.CHAR_TYPE; - case 'S' -> Type.SHORT_TYPE; - case 'I' -> Type.INTEGER_TYPE; - case 'J' -> Type.LONG_TYPE; - case 'F' -> Type.FLOAT_TYPE; - case 'D' -> Type.DOUBLE_TYPE; - default -> Type.TOP_TYPE; - }; - } - return Type.referenceType(comp); - } - return Type.TOP_TYPE; - } } final ArrayList stack = new ArrayList<>(); final ArrayList locals = new ArrayList<>(); + final StateMachineBuilder smb; + LineNumber current_line_number = null; + int bci = 0; HashMap stackMapFrames = new HashMap<>(); - final StateMachineBuilder smb; FrameTracker(StateMachineBuilder smb, CodeModel com) { this.smb = smb; int offset = 0; - - for (var param : smb.params) { if(param == CD_long){ setLocal2(offset, Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); @@ -246,6 +216,11 @@ public class FrameTracker { : Type.referenceType(desc)); } + FrameTracker pushStack(Type... types) { + stack.addAll(Arrays.asList(types)); + return this; + } + FrameTracker pushStack(Type type) { stack.add(type); return this; @@ -315,10 +290,8 @@ public class FrameTracker { case String _ -> pushStack(Type.STRING_TYPE); case ClassDesc desc -> pushStack(desc); case DynamicConstantDesc dynamicConstantDesc -> pushStack(dynamicConstantDesc.constantType()); - case MethodHandleDesc methodHandleDesc -> - throw new RuntimeException(); - case MethodTypeDesc methodTypeDesc -> - throw new RuntimeException(); + case MethodHandleDesc methodHandleDesc -> pushStack(Type.METHOD_HANDLE_TYPE); + case MethodTypeDesc methodTypeDesc -> pushStack(Type.METHOD_TYPE); } } case ConvertInstruction c -> decStack(c.fromType().slotSize()).pushStack(c.toType().upperBound()); @@ -341,10 +314,27 @@ public class FrameTracker { decStack(TypeKind.from(param).slotSize()); pushStack(i.typeSymbol().returnType()); } - case InvokeInstruction i when i.opcode() == Opcode.INVOKESPECIAL && i.name().equalsString(INIT_NAME) -> { - for(var param : i.typeSymbol().parameterArray()) + case InvokeInstruction ii when ii.opcode() == Opcode.INVOKESPECIAL && ii.name().equalsString(INIT_NAME) -> { + for(var param : ii.typeSymbol().parameterArray()) decStack(TypeKind.from(param).slotSize()); var ty = popStack(); + if(ty.tag == ITEM_UNINITIALIZED){ + if(ty.sym!=null&&!ty.sym.equals(ii.owner().asSymbol())) + throw new RuntimeException(); + var init_type = ii.owner().asSymbol(); + for(int i = 0; i < stack.size(); i ++){ + if(stack.get(i).bci==ty.bci&&stack.get(i).tag==ITEM_UNINITIALIZED){ + stack.set(i, Type.referenceType(init_type)); + } + } + for(int i = 0; i < locals.size(); i ++){ + if(locals.get(i).bci==ty.bci&&locals.get(i).tag==ITEM_UNINITIALIZED){ + locals.set(i, Type.referenceType(init_type)); + } + } + }else{ + throw new RuntimeException(); + } } case InvokeInstruction i -> { for(var param : i.typeSymbol().parameterArray()) @@ -359,7 +349,7 @@ public class FrameTracker { case LookupSwitchInstruction ls -> popStack(); case MonitorInstruction m -> popStack(); case NewMultiArrayInstruction nma -> decStack(nma.dimensions()).pushStack(nma.arrayType().asSymbol()); - case NewObjectInstruction no -> pushStack(no.className().asSymbol()); + case NewObjectInstruction no -> pushStack(Type.uninitializedType(no.className().asSymbol(), bci)); case NewPrimitiveArrayInstruction npa -> decStack(1).pushStack(npa.typeKind().upperBound().arrayType()); case NewReferenceArrayInstruction nra -> decStack(1).pushStack(nra.componentType().asSymbol().arrayType()); case NopInstruction n -> {} @@ -388,48 +378,48 @@ public class FrameTracker { } } case StackInstruction s -> { - Type type1, type2, type3, type4; switch(s.opcode()){ case POP -> decStack(1); case POP2 -> decStack(2); - case DUP -> - pushStack(type1 = popStack()).pushStack(type1); + case DUP -> { + var type1 = popStack(); + pushStack(type1, type1); + } case DUP_X1 -> { - type1 = popStack(); - type2 = popStack(); - pushStack(type1).pushStack(type2).pushStack(type1); + var type1 = popStack(); + var type2 = popStack(); + pushStack(type1, type2, type1); } case DUP_X2 -> { - type1 = popStack(); - type2 = popStack(); - type3 = popStack(); - pushStack(type1).pushStack(type3).pushStack(type2).pushStack(type1); + var type1 = popStack(); + var type2 = popStack(); + var type3 = popStack(); + pushStack(type1, type3, type2, type1); } case DUP2 -> { - type1 = popStack(); - type2 = popStack(); - pushStack(type2).pushStack(type1).pushStack(type2).pushStack(type1); + var type1 = popStack(); + var type2 = popStack(); + pushStack(type2, type1, type2, type1); } case DUP2_X1 -> { - type1 = popStack(); - type2 = popStack(); - type3 = popStack(); - pushStack(type2).pushStack(type1).pushStack(type3).pushStack(type2).pushStack(type1); + var type1 = popStack(); + var type2 = popStack(); + var type3 = popStack(); + pushStack(type2, type1, type3, type2, type1); } case DUP2_X2 -> { - type1 = popStack(); - type2 = popStack(); - type3 = popStack(); - type4 = popStack(); - pushStack(type2).pushStack(type1).pushStack(type4).pushStack(type3).pushStack(type2).pushStack(type1); + var type1 = popStack(); + var type2 = popStack(); + var type3 = popStack(); + var type4 = popStack(); + pushStack(type2, type1, type4, type3, type2, type1); } case SWAP -> { - type1 = popStack(); - type2 = popStack(); - pushStack(type1); - pushStack(type2); + var type1 = popStack(); + var type2 = popStack(); + pushStack(type1, type2); } default -> throw new RuntimeException(); } @@ -448,10 +438,54 @@ public class FrameTracker { case DiscontinuedInstruction d -> throw new IllegalStateException(d.toString()); default -> throw new IllegalStateException(); } + bci += ins.sizeInBytes(); + } + case PseudoInstruction p -> { + switch(p){ + case CharacterRange cr -> { + } + case ExceptionCatch ec -> { + } + case LabelTarget lt -> { + } + case LineNumber ln -> current_line_number = ln; + case LocalVariable lv -> { + } + case LocalVariableType lvt -> { + } + default -> {} + } } - case PseudoInstruction _ -> {} case RuntimeInvisibleTypeAnnotationsAttribute _ -> {} - case RuntimeVisibleTypeAnnotationsAttribute _ -> {} + case RuntimeVisibleTypeAnnotationsAttribute tas -> { + for(var ta : tas.annotations()){ + switch(ta.targetInfo()){ + case TypeAnnotation.CatchTarget ct -> { + } + case TypeAnnotation.EmptyTarget et -> { + } + case TypeAnnotation.FormalParameterTarget fpt -> { + } + case TypeAnnotation.LocalVarTarget lvt -> { + for(var el : lvt.table()){ + el.endLabel(); + } + } + case TypeAnnotation.OffsetTarget ot -> { + } + case TypeAnnotation.SupertypeTarget stt -> { + } + case TypeAnnotation.ThrowsTarget tt -> { + } + case TypeAnnotation.TypeArgumentTarget tat -> { + } + case TypeAnnotation.TypeParameterBoundTarget tpbt -> { + } + case TypeAnnotation.TypeParameterTarget tpt -> { + } + } + } + } case CustomAttribute _ -> {} case StackMapTableAttribute _ -> {} } diff --git a/src/generators/loadtime/StateMachineBuilder.java b/src/generators/loadtime/StateMachineBuilder.java index d07bc7e..f256ac4 100644 --- a/src/generators/loadtime/StateMachineBuilder.java +++ b/src/generators/loadtime/StateMachineBuilder.java @@ -29,7 +29,7 @@ public abstract class StateMachineBuilder { ArrayList lstate = new ArrayList<>(); - private final ArrayList frames = new ArrayList<>(); + private final ArrayList frames; protected final HashMap smmap = new HashMap<>(); @@ -77,15 +77,7 @@ public abstract class StateMachineBuilder { this.MTD_init = MethodTypeDesc.of(ConstantDescs.CD_void, params); this.paramSlotOff = Arrays.stream(params).mapToInt(p -> TypeKind.from(p).slotSize()).sum(); - var lt = new FrameTracker(this, src_com); - for(var coe : src_com){ - if(coe instanceof Instruction) { - frames.add(new Frame(lt.locals(), lt.stack())); -// System.out.println(frames.getLast() + " " + coe); - } - lt.encounter(coe); - } - frames.add(new Frame(lt.locals(), lt.stack())); + frames = FrameTracker.frames(this, src_com); } public record WithFrame(CodeElement coe, Frame frame){} @@ -236,7 +228,10 @@ public abstract class StateMachineBuilder { for (var wf : with_frames()) { if (wf.coe() instanceof InvokeInstruction is) { var h = smmap.get(new SpecialMethod(is.owner().asSymbol(), is.name().stringValue(), is.typeSymbol())); - if(h!=null) handlers.get(i++).build_prelude(this, cob, wf.frame()); + if(h!=null) { + if(wf.frame.line()!=null)cob.with(wf.frame.line()); + handlers.get(i++).build_prelude(this, cob, wf.frame()); + } } } }