added support for parameter cancellation

This commit is contained in:
Parker TenBroeck 2025-05-05 16:25:33 -04:00
parent f0bc740e82
commit 051d5142d1
10 changed files with 154 additions and 64 deletions

View file

@ -20,6 +20,14 @@ tasks.withType<JavaCompile> {
options.compilerArgs.add("--enable-preview") options.compilerArgs.add("--enable-preview")
} }
tasks.register<JavaExec>("basic"){
javaLauncher.set(javaToolchains.launcherFor(java.toolchain))
jvmArgs("--enable-preview")
group = "Demos"
mainClass = "basic.Main"
classpath = sourceSets["main"].runtimeClasspath
}
tasks.register<JavaExec>("lexer"){ tasks.register<JavaExec>("lexer"){
javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) javaLauncher.set(javaToolchains.launcherFor(java.toolchain))
jvmArgs("--enable-preview") jvmArgs("--enable-preview")

View file

@ -0,0 +1,7 @@
package basic;
import com.parkertenbroeck.future.Future;
import com.parkertenbroeck.future.Waker;
public class Futures {
}

View file

@ -0,0 +1,17 @@
package basic;
import com.parkertenbroeck.generator.Gen;
public class Gens {
public static Gen<Long, Void> primes(){
long number = 1;
Gen.yield(2L);
outer: while(true){
number += 2;
for(long i=2; i <= Math.sqrt(number); i ++){
if(number%i==0)continue outer;
}
Gen.yield(number);
}
}
}

View file

@ -0,0 +1,20 @@
package basic;
import com.parkertenbroeck.bcsm.RT;
import com.parkertenbroeck.bcsm.loadtime.StateMachineClassLoader;
import com.parkertenbroeck.generator.Gen;
public class Main {
public static void main(String[] args) {
RT.runWithStateMachines(StateMachineClassLoader.Config.builtin().write_classes("build/modified/generators/"), (Object) args);
primes();
}
static void primes(){
var gen = Gens.primes();
while(gen.next() instanceof Gen.Yield(var tok)) {
System.out.println(tok);
}
}
}

View file

@ -6,6 +6,7 @@ import com.parkertenbroeck.async_runtime.io.net.ServerSocket;
import com.parkertenbroeck.async_runtime.io.net.Socket; import com.parkertenbroeck.async_runtime.io.net.Socket;
import com.parkertenbroeck.future.Future; import com.parkertenbroeck.future.Future;
import com.parkertenbroeck.bcsm.loadtime.future.Cancellation; import com.parkertenbroeck.bcsm.loadtime.future.Cancellation;
import com.parkertenbroeck.future.Waker;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;

View file

@ -1,6 +1,5 @@
package com.parkertenbroeck.bcsm.loadtime; package com.parkertenbroeck.bcsm.loadtime;
import java.lang.classfile.*; import java.lang.classfile.*;
import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeInvisibleTypeAnnotationsAttribute;
import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute; import java.lang.classfile.attribute.RuntimeVisibleTypeAnnotationsAttribute;
@ -8,6 +7,7 @@ import java.lang.classfile.attribute.StackMapFrameInfo;
import java.lang.classfile.attribute.StackMapTableAttribute; import java.lang.classfile.attribute.StackMapTableAttribute;
import java.lang.classfile.instruction.*; import java.lang.classfile.instruction.*;
import java.lang.constant.*; import java.lang.constant.*;
import java.lang.reflect.AccessFlag;
import java.util.*; import java.util.*;
import static java.lang.constant.ConstantDescs.*; import static java.lang.constant.ConstantDescs.*;
@ -180,7 +180,11 @@ public class FrameTracker {
this.smb = smb; this.smb = smb;
int offset = 0; int offset = 0;
for (var param : smb.params) {
for (int i = 0; i < smb.params.length; i++) {
var param = smb.params[i];
if (param == CD_long) { if (param == CD_long) {
setLocal2(offset, Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); setLocal2(offset, Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE);
} else if (param == CD_double) { } else if (param == CD_double) {
@ -412,6 +416,11 @@ public class FrameTracker {
decStack(4).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); decStack(4).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE);
case DNEG -> case DNEG ->
decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE); decStack(2).pushStack(Type.DOUBLE_TYPE, Type.DOUBLE2_TYPE);
case LCMP, DCMPL, DCMPG ->
decStack(4).pushStack(Type.INTEGER_TYPE);
case FCMPL, FCMPG ->
decStack(2).pushStack(Type.INTEGER_TYPE);
default -> throw new RuntimeException(); default -> throw new RuntimeException();
} }
} }

View file

@ -54,7 +54,7 @@ public class SavedStateTracker {
} }
public LocalState load_param(int slot) { public LocalState get_saved_local(int slot) {
for(var saved : saved){ for(var saved : saved){
if (saved instanceof LocalState(var name, var desc, int s) && s == slot) { if (saved instanceof LocalState(var name, var desc, int s) && s == slot) {
return (LocalState) saved; return (LocalState) saved;

View file

@ -12,6 +12,8 @@ import java.lang.reflect.AccessFlag;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.lang.constant.ConstantDescs.*;
public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> { public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
public final static String PARAM_PREFIX = "param_"; public final static String PARAM_PREFIX = "param_";
public final static String LOCAL_PREFIX = "local_"; public final static String LOCAL_PREFIX = "local_";
@ -33,6 +35,8 @@ public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
protected final HashMap<SpecialMethod, SpecialMethodBuilder<T>> smmap = new HashMap<>(); protected final HashMap<SpecialMethod, SpecialMethodBuilder<T>> smmap = new HashMap<>();
public record ParameterVariableAnnotation(Annotation annotation, String param, ClassDesc desc){}
protected final List<ParameterVariableAnnotation> parameterVariableAnnotations = new ArrayList<>();
record LState(String name, ClassDesc cd) { record LState(String name, ClassDesc cd) {
@ -63,6 +67,7 @@ public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
.replace("/", "___") .replace("/", "___")
.replace(";", "____") .replace(";", "____")
.replace("[", "_____") .replace("[", "_____")
.replace(".", "______")
) )
.collect(Collectors.joining("$")); .collect(Collectors.joining("$"));
innerClassName = method_name + "______" + param_cnd; innerClassName = method_name + "______" + param_cnd;
@ -73,6 +78,25 @@ public abstract class StateMachineBuilder<T extends StateMachineBuilder<T>> {
this.MTD_init = MethodTypeDesc.of(ConstantDescs.CD_void, params); this.MTD_init = MethodTypeDesc.of(ConstantDescs.CD_void, params);
this.paramSlotOff = Arrays.stream(params).mapToInt(p -> TypeKind.from(p).slotSize()).sum(); this.paramSlotOff = Arrays.stream(params).mapToInt(p -> TypeKind.from(p).slotSize()).sum();
ArrayList<List<Annotation>> param_attrs;
var param_ann = src_mem.findAttributes(Attributes.runtimeVisibleParameterAnnotations());
if(!param_ann.isEmpty()) {
param_attrs = new ArrayList<>(param_ann.getFirst().parameterAnnotations());
if(!src_mem.flags().has(AccessFlag.STATIC)){
param_attrs.addFirst(List.of());
}
}else
param_attrs = new ArrayList<>(Collections.nCopies((src_mem.flags().has(AccessFlag.STATIC)?0:1)+src_mem.methodTypeSymbol().parameterList().size(), List.of()));
int offset = 0;
for (int i = 0; i < params.length; i++) {
var param = params[i];
for(var attr : param_attrs.get(i))
parameterVariableAnnotations.add(new ParameterVariableAnnotation(attr, StateMachineBuilder.PARAM_PREFIX + offset, param));
offset += TypeKind.from(param).slotSize();
}
frames = FrameTracker.frames(this, src_com); frames = FrameTracker.frames(this, src_com);
} }

View file

@ -3,7 +3,7 @@ package com.parkertenbroeck.bcsm.loadtime.future;
import java.lang.annotation.*; import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE}) @Target({ElementType.TYPE_USE, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER})
public @interface Cancellation { public @interface Cancellation {
String value() default "cancel"; String value() default "cancel";
} }

View file

@ -183,47 +183,47 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
ArrayList<Consumer<CodeBuilder>> field_cancellation_behavior = new ArrayList<>(); ArrayList<Consumer<CodeBuilder>> field_cancellation_behavior = new ArrayList<>();
for(var ann : frame.local_annotations()){ for(var ann : frame.local_annotations()){
if(ann.annotation().classSymbol().descriptorString().equals(Cancellation.class.descriptorString())){ if(ann.annotation().classSymbol().descriptorString().equals(Cancellation.class.descriptorString())){
var param = sst.load_param(ann.slot()-paramSlotOff+2); var param = sst.get_saved_local(ann.slot()-paramSlotOff+2);
ClassDesc owner = frame.locals()[ann.slot()].sym(); ClassDesc owner = frame.locals()[ann.slot()].sym();
field_cancellation_behavior.add(make_canceler(param.name(), param.desc(), methodFromCancellationAnnotation(ann.annotation(), owner)));
}
}
cancellation_behavior.put(state.id(), cob -> {
for(var fcb : field_cancellation_behavior) fcb.accept(cob);
});
}
private Method methodFromCancellationAnnotation(Annotation annotation, ClassDesc owner){
String name = "cancel"; String name = "cancel";
for(var el : ann.annotation().elements()){ for(var el : annotation.elements()){
switch(el.name().stringValue()){ if (el.name().stringValue().equals("value")) {
case "value" -> name = annotationStringValue(el.value()); name = annotationStringValue(el.value());
case "owner" -> owner = annotationClassValue(el.value());
} }
} }
if(!owner.isClassOrInterface()){
throw new RuntimeException("Owner " + owner + " is not a class/interface cannot be used here");
}
boolean is_interface;
TypeKind ret;
try{ try{
Class<?> clazz = getClass().getClassLoader().loadClass(owner.descriptorString().replace("/", ".").replace(";", "").substring(1)); Class<?> clazz = getClass().getClassLoader().loadClass(owner.descriptorString().replace("/", ".").replace(";", "").substring(1));
var method = findMethod(clazz, name); var method = findMethod(clazz, name);
if(method==null){ if(method==null)
throw new RuntimeException("Cannot find method '"+name+"' for class " + clazz); throw new RuntimeException("Cannot find method '"+name+"' for class " + clazz);
}
clazz = method.getDeclaringClass(); return method;
owner = ClassDesc.ofDescriptor(clazz.descriptorString());
is_interface = clazz.isInterface();
ret = TypeKind.from(ClassDesc.ofDescriptor(method.getReturnType().descriptorString()));
}catch (Exception e){ }catch (Exception e){
throw new RuntimeException(e); throw new RuntimeException(e);
} }
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));
} }
private Consumer<CodeBuilder> make_canceler(String field_name, ClassDesc field_desc, Method method) {
var owner = ClassDesc.ofDescriptor(method.getDeclaringClass().descriptorString());
return cob -> {
cob.trying(tcob -> {
tcob.aload(0).getfield(CD_this, field_name, field_desc);
if (method.getDeclaringClass().isInterface()) {
tcob.invokeinterface(owner, method.getName(), MethodTypeDesc.of(ConstantDescs.CD_void));
} else {
tcob.invokevirtual(owner, method.getName(), MethodTypeDesc.of(ConstantDescs.CD_void));
}
var ret = TypeKind.from(method.getReturnType());
if (ret.slotSize() == 1) tcob.pop(); if (ret.slotSize() == 1) tcob.pop();
if (ret.slotSize() == 2) tcob.pop2(); if (ret.slotSize() == 2) tcob.pop2();
}, cb -> cb.catchingAll(ccob -> { }, cb -> cb.catchingAll(ccob -> {
@ -234,12 +234,7 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
bcob.astore(2); bcob.astore(2);
}); });
})); }));
}); };
}
}
cancellation_behavior.put(state.id(), cob -> {
for(var fcb : field_cancellation_behavior) fcb.accept(cob);
});
} }
private static Method findMethod(Class<?> owner, String name){ private static Method findMethod(Class<?> owner, String name){
@ -274,8 +269,13 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
clb.withMethod("cancel", MethodTypeDesc.of(ConstantDescs.CD_void), ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> { clb.withMethod("cancel", MethodTypeDesc.of(ConstantDescs.CD_void), ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> {
this.synchronized_start(cob); this.synchronized_start(cob);
cob.aload(0).getfield(CD_this, STATE_NAME, ConstantDescs.CD_int).istore(1);// state cob.aload(0).getfield(CD_this, STATE_NAME, ConstantDescs.CD_int).istore(1);// state
cob.aconst_null().astore(2);// exception cob.aconst_null().astore(2);// exception
cob.iload(1).loadConstant(-1).ifThen(Opcode.IF_ICMPEQ, bob -> {
this.synchronized_exit(bob);
bob.return_();
});
cob.aload(0).loadConstant(-1).putfield(CD_this, STATE_NAME, ConstantDescs.CD_int); cob.aload(0).loadConstant(-1).putfield(CD_this, STATE_NAME, ConstantDescs.CD_int);
cob.aload(0).getfield(CD_this, AWAITING_FIELD_NAME, CD_Future).ifThen(Opcode.IFNONNULL, boc -> { cob.aload(0).getfield(CD_this, AWAITING_FIELD_NAME, CD_Future).ifThen(Opcode.IFNONNULL, boc -> {
@ -310,6 +310,10 @@ public class FutureSMBuilder extends StateMachineBuilder<FutureSMBuilder> {
cob.labelBinding(end); cob.labelBinding(end);
} }
for(var la : this.parameterVariableAnnotations){
make_canceler(la.param(), la.desc(), methodFromCancellationAnnotation(la.annotation(), la.desc())).accept(cob);
}
this.synchronized_exit(cob); this.synchronized_exit(cob);
cob.aload(2).ifThen(Opcode.IFNONNULL, bcob -> { cob.aload(2).ifThen(Opcode.IFNONNULL, bcob -> {