mirror of
https://github.com/ParkerTenBroeck/coroutines.git
synced 2026-06-07 05:08:51 -04:00
improved usability of cancellation annotations
This commit is contained in:
parent
baf0e86f39
commit
ce4dd70407
4 changed files with 133 additions and 98 deletions
|
|
@ -13,24 +13,32 @@ public class Main implements Runnable {
|
|||
RT.runWithGeneratorSupport(Main.class);
|
||||
}
|
||||
|
||||
class Meow implements AutoCloseable{
|
||||
{
|
||||
System.out.println("CREATED");
|
||||
}
|
||||
|
||||
static class Meow implements AutoCloseable{
|
||||
{System.out.println("CREATED");}
|
||||
@Override
|
||||
public void close() {
|
||||
System.out.println("CLOSED");
|
||||
}
|
||||
public static AutoCloseable test(){
|
||||
return new Meow();
|
||||
}
|
||||
}
|
||||
synchronized Future<Void, RuntimeException> nya(){
|
||||
|
||||
Future<Void, RuntimeException> nya(){
|
||||
try(@Cancellation("close") var nya = new Meow()){
|
||||
try(@Cancellation("close") var nya = Meow.test()){
|
||||
System.out.println("POLLED");
|
||||
Future.yield();
|
||||
System.out.println("POLLED");
|
||||
Future.yield();
|
||||
int beep = 1;
|
||||
System.out.println("POLLED");
|
||||
Future.yield();
|
||||
System.out.println("POLLED");
|
||||
Future.yield();
|
||||
return Future.ret(null);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ public interface Future<R, E extends Throwable> {
|
|||
throw new RuntimeException("NO!");
|
||||
}
|
||||
|
||||
default void cancel(){}
|
||||
default void cancel() throws E{}
|
||||
|
||||
default Future<R, E> non_cancelable(){
|
||||
return this::poll;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
package generators.loadtime.future;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.annotation.*;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE})
|
||||
public @interface Cancellation {
|
||||
String value() default "cancel";
|
||||
Class<?> param() default void.class;
|
||||
Class<?> ret() default void.class;
|
||||
Class<?> owner() default void.class;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,10 @@ import future.Waker;
|
|||
import generators.loadtime.*;
|
||||
|
||||
import java.lang.classfile.*;
|
||||
import java.lang.classfile.constantpool.InterfaceMethodRefEntry;
|
||||
import java.lang.classfile.constantpool.MethodRefEntry;
|
||||
import java.lang.classfile.instruction.SwitchCase;
|
||||
import java.lang.constant.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
|
||||
|
|
@ -166,39 +163,16 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
|
|||
}
|
||||
}
|
||||
|
||||
private String fromAnnValue(AnnotationValue value){
|
||||
switch(value){
|
||||
case AnnotationValue.OfAnnotation ofAnnotation -> {
|
||||
}
|
||||
case AnnotationValue.OfArray ofArray -> {
|
||||
}
|
||||
case AnnotationValue.OfClass ofClass -> {
|
||||
}
|
||||
case AnnotationValue.OfConstant ofConstant -> {
|
||||
switch(ofConstant){
|
||||
case AnnotationValue.OfBoolean ofBoolean -> {
|
||||
}
|
||||
case AnnotationValue.OfByte ofByte -> {
|
||||
}
|
||||
case AnnotationValue.OfChar ofChar -> {
|
||||
}
|
||||
case AnnotationValue.OfDouble ofDouble -> {
|
||||
}
|
||||
case AnnotationValue.OfFloat ofFloat -> {
|
||||
}
|
||||
case AnnotationValue.OfInt ofInt -> {
|
||||
}
|
||||
case AnnotationValue.OfLong ofLong -> {
|
||||
}
|
||||
case AnnotationValue.OfShort ofShort -> {
|
||||
}
|
||||
case AnnotationValue.OfString ofString -> {
|
||||
private String annotationStringValue(AnnotationValue value){
|
||||
if (value instanceof AnnotationValue.OfString ofString) {
|
||||
return ofString.stringValue();
|
||||
}
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
case AnnotationValue.OfEnum ofEnum -> {
|
||||
}
|
||||
|
||||
private ClassDesc annotationClassValue(AnnotationValue value){
|
||||
if (value instanceof AnnotationValue.OfClass ofConstant) {
|
||||
return ofConstant.classSymbol();
|
||||
}
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
|
@ -206,7 +180,7 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
|
|||
private void yielding_state(StateBuilder.State state, Frame frame, SavedStateTracker sst){
|
||||
if(frame.local_annotations().length==0)return;
|
||||
|
||||
ArrayList<Consumer<CodeBuilder>> stuff = new ArrayList<>();
|
||||
ArrayList<Consumer<CodeBuilder>> field_cancellation_behavior = new ArrayList<>();
|
||||
for(var ann : frame.local_annotations()){
|
||||
if(ann.annotation().classSymbol().descriptorString().equals(Cancellation.class.descriptorString())){
|
||||
var param = sst.load_param(ann.slot()-paramSlotOff+2);
|
||||
|
|
@ -214,34 +188,82 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
|
|||
String name = "cancel";
|
||||
for(var el : ann.annotation().elements()){
|
||||
switch(el.name().stringValue()){
|
||||
case "value" -> name = fromAnnValue(el.value());
|
||||
case "owner" -> {}
|
||||
case "ret" -> {}
|
||||
case "value" -> name = annotationStringValue(el.value());
|
||||
case "owner" -> owner = annotationClassValue(el.value());
|
||||
}
|
||||
el.name().equalsString("value");
|
||||
}
|
||||
String final_name = name;
|
||||
stuff.add(cob -> {
|
||||
cob.trying(tcob -> {
|
||||
tcob.aload(0).getfield(CD_this, param.name(), param.desc());
|
||||
|
||||
if(!owner.isClassOrInterface()){
|
||||
throw new RuntimeException("Owner " + owner + " is not a class/interface cannot be used here");
|
||||
}
|
||||
boolean is_interface;
|
||||
TypeKind ret;
|
||||
try{
|
||||
if(FutureSMBuilder.class.getClassLoader().loadClass(owner.displayName()).isInterface()){
|
||||
tcob.invokeinterface(owner, final_name, MethodTypeDesc.of(ConstantDescs.CD_void));
|
||||
}else{
|
||||
tcob.invokevirtual(owner, final_name, MethodTypeDesc.of(ConstantDescs.CD_void));
|
||||
Class<?> clazz = getClass().getClassLoader().loadClass(owner.descriptorString().replace("/", ".").replace(";", "").substring(1));
|
||||
|
||||
var method = findMethod(clazz, name);
|
||||
if(method==null){
|
||||
throw new RuntimeException("Cannot find method '"+name+"' for class " + clazz);
|
||||
}
|
||||
clazz = method.getDeclaringClass();
|
||||
owner = ClassDesc.ofDescriptor(clazz.descriptorString());
|
||||
is_interface = clazz.isInterface();
|
||||
ret = TypeKind.from(ClassDesc.ofDescriptor(method.getReturnType().descriptorString()));
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}, cb -> cb.catchingAll(ccob -> ccob.pop()));
|
||||
|
||||
String final_name = name;
|
||||
ClassDesc final_owner = owner;
|
||||
|
||||
field_cancellation_behavior.add(cob -> {
|
||||
cob.trying(tcob -> {
|
||||
tcob.aload(0).getfield(CD_this, param.name(), param.desc());
|
||||
if(is_interface){
|
||||
tcob.invokeinterface(final_owner, final_name, MethodTypeDesc.of(ConstantDescs.CD_void));
|
||||
}else{
|
||||
tcob.invokevirtual(final_owner, final_name, MethodTypeDesc.of(ConstantDescs.CD_void));
|
||||
}
|
||||
if(ret.slotSize()==1)tcob.pop();
|
||||
if(ret.slotSize()==2)tcob.pop2();
|
||||
}, cb -> cb.catchingAll(ccob -> {
|
||||
ccob.aload(2).ifThenElse(Opcode.IFNONNULL, bcob -> {
|
||||
bcob.aload(2).swap();
|
||||
addSuppressed(bcob);
|
||||
}, bcob -> {
|
||||
bcob.astore(2);
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
cancellation_behavior.put(state.id(), cob -> {
|
||||
for(var thing : stuff) thing.accept(cob);
|
||||
for(var fcb : field_cancellation_behavior) fcb.accept(cob);
|
||||
});
|
||||
}
|
||||
|
||||
private static Method findMethod(Class<?> owner, String name){
|
||||
try{
|
||||
return owner.getDeclaredMethod(name);
|
||||
}catch (Exception ignore){}
|
||||
|
||||
if(owner.equals(Object.class))return null;
|
||||
|
||||
for(var iface : owner.getInterfaces()){
|
||||
var method = findMethod(iface, name);
|
||||
if(method!=null)return method;
|
||||
}
|
||||
|
||||
if(!owner.isInterface()){
|
||||
Class<?> sup = owner.getSuperclass();
|
||||
if(sup!=null){
|
||||
return findMethod(sup, name);
|
||||
}
|
||||
}
|
||||
|
||||
return findMethod(Object.class, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildStateMachineMethod(ClassBuilder clb){
|
||||
clb.withInterfaces(List.of(clb.constantPool().classEntry(CD_Future)));
|
||||
|
|
@ -252,45 +274,56 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
|
|||
clb.withMethod("cancel", MethodTypeDesc.of(ConstantDescs.CD_void), ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> {
|
||||
|
||||
this.synchronized_start(cob);
|
||||
cob.trying(tcob -> {
|
||||
tcob.aload(0).getfield(CD_this, STATE_NAME, ConstantDescs.CD_int).istore(1);
|
||||
tcob.aload(0).loadConstant(-1).putfield(CD_this, STATE_NAME, ConstantDescs.CD_int);
|
||||
cob.aload(0).getfield(CD_this, STATE_NAME, ConstantDescs.CD_int).istore(1);// state
|
||||
cob.aconst_null().astore(2);// exception
|
||||
cob.aload(0).loadConstant(-1).putfield(CD_this, STATE_NAME, ConstantDescs.CD_int);
|
||||
|
||||
tcob.aload(0).getfield(CD_this, AWAITING_FIELD_NAME, CD_Future).ifThen(Opcode.IFNONNULL, boc -> {
|
||||
boc.aload(0).getfield(CD_this, AWAITING_FIELD_NAME, CD_Future)
|
||||
.invokeinterface(CD_Future, "cancel", MethodTypeDesc.of(ConstantDescs.CD_void))
|
||||
.aload(0).aconst_null().putfield(CD_this, AWAITING_FIELD_NAME, CD_Future);
|
||||
cob.aload(0).getfield(CD_this, AWAITING_FIELD_NAME, CD_Future).ifThen(Opcode.IFNONNULL, boc -> {
|
||||
boc.trying(tcob -> {
|
||||
tcob.aload(0).getfield(CD_this, AWAITING_FIELD_NAME, CD_Future)
|
||||
.aload(0).aconst_null().putfield(CD_this, AWAITING_FIELD_NAME, CD_Future)
|
||||
.invokeinterface(CD_Future, "cancel", MethodTypeDesc.of(ConstantDescs.CD_void));
|
||||
|
||||
}, cb -> {
|
||||
cb.catchingAll(ccob -> {
|
||||
ccob.aload(2).ifThenElse(Opcode.IFNONNULL, bcob -> {
|
||||
bcob.aload(2).swap();
|
||||
addSuppressed(bcob);
|
||||
}, bcob -> {
|
||||
bcob.astore(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if(!cancellation_behavior.isEmpty()){
|
||||
var states = cancellation_behavior.entrySet().stream().toList();
|
||||
var cases = states.stream().map(v -> SwitchCase.of(v.getKey(), tcob.newLabel())).toList();
|
||||
tcob.iload(1);
|
||||
var end = tcob.newLabel();
|
||||
tcob.tableswitch(end, cases);
|
||||
var cases = states.stream().map(v -> SwitchCase.of(v.getKey(), cob.newLabel())).toList();
|
||||
cob.iload(1);
|
||||
var end = cob.newLabel();
|
||||
cob.tableswitch(end, cases);
|
||||
for(int i = 0; i < states.size(); i ++){
|
||||
tcob.labelBinding(cases.get(i).target());
|
||||
states.get(i).getValue().accept(tcob);
|
||||
tcob.goto_(end);
|
||||
cob.labelBinding(cases.get(i).target());
|
||||
states.get(i).getValue().accept(cob);
|
||||
cob.goto_(end);
|
||||
}
|
||||
tcob.labelBinding(end);
|
||||
cob.labelBinding(end);
|
||||
}
|
||||
|
||||
|
||||
this.synchronized_exit(tcob);
|
||||
tcob.return_();
|
||||
}, cb -> {
|
||||
cb.catchingAll(ccob -> {
|
||||
ccob.pop();
|
||||
this.synchronized_exit(ccob);
|
||||
ccob.return_();
|
||||
this.synchronized_exit(cob);
|
||||
cob.aload(2).ifThen(Opcode.IFNONNULL, bcob -> {
|
||||
bcob.aload(2).athrow();
|
||||
});
|
||||
});
|
||||
|
||||
cob.return_();
|
||||
}));
|
||||
clb.withField(AWAITING_FIELD_NAME, CD_Future, ClassFile.ACC_PRIVATE);
|
||||
}
|
||||
|
||||
private void addSuppressed(CodeBuilder cob) {
|
||||
cob.invokevirtual(ClassDesc.ofDescriptor(Throwable.class.descriptorString()), "addSuppressed", MethodTypeDesc.of(ConstantDescs.CD_void, ClassDesc.ofDescriptor(Throwable.class.descriptorString())));
|
||||
}
|
||||
|
||||
public FutureSMBuilder(ClassModel src_clm, MethodModel src_mem, CodeModel src_com) {
|
||||
super(src_clm, src_mem, src_com, "Fut");
|
||||
smmap.put(new SpecialMethod(CD_Future, "await", MTD_Obj), AwaitHandler::new);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue