diff --git a/src/Main.java b/src/Main.java index 4c1762d..292420a 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,6 +1,8 @@ import generator.RT; import generator.gen.Gen; +import java.util.function.Supplier; + public class Main implements Runnable { public static void main(String[] args) { RT.runWithGeneratorSupport(Main.class); @@ -10,6 +12,23 @@ public class Main implements Runnable { public void run() { // await(); lexer(); +// lambda(); + } + + void lambda(){ + var gen = ((Supplier>)() -> { + Gen.yield(12); + return Gen.ret("hello"); + }).get(); + + while(true) { + var next = gen.next(); + if(next instanceof Gen.Yield(var e)) System.out.println(e); + else if(next instanceof Gen.Ret(var ret)){ + System.out.println(ret); + break; + } + } } void await(){ @@ -22,8 +41,11 @@ public class Main implements Runnable { break; } } + + Runnable meow = () -> {}; } + void lexer(){ var gen = Lexer.parse("f7(x,y,z,w, u,v, othersIg) = v-(x*y+y+ln(z)^2*sin(z*pi/2))/(w*u)+sqrt(othersIg*120e-1)"); // var gen = Examples.test(new double[]{1,2,3,4}); diff --git a/src/generator/future/Future.java b/src/generator/future/Future.java index d1c2c06..b8293be 100644 --- a/src/generator/future/Future.java +++ b/src/generator/future/Future.java @@ -11,6 +11,10 @@ public interface Future { throw new RuntimeException(); } + static Future ret(R r){ + throw new RuntimeException(); + } + final class Pending{ private static final Pending INSTANCE = new Pending(); private Pending(){} diff --git a/src/generator/runtime/GeneratorBuilder.java b/src/generator/runtime/GeneratorBuilder.java deleted file mode 100644 index 287b232..0000000 --- a/src/generator/runtime/GeneratorBuilder.java +++ /dev/null @@ -1,104 +0,0 @@ -package generator.runtime; - -import generator.gen.Gen; - -import java.lang.classfile.*; -import java.lang.classfile.attribute.InnerClassInfo; -import java.lang.classfile.attribute.InnerClassesAttribute; -import java.lang.classfile.attribute.NestHostAttribute; -import java.lang.constant.ClassDesc; -import java.lang.constant.ConstantDescs; -import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.AccessFlag; -import java.util.*; - -public class GeneratorBuilder { - public final static String PARAM_PREFIX = "param_"; - public final static String LOCAL_PREFIX = "local_"; - public final static String STACK_PREFIX = "stack_"; - public final static String STATE_NAME = "state"; - - public final static ClassDesc CD_Gen = ClassDesc.ofDescriptor(Gen.class.descriptorString()); - public final static ClassDesc CD_Res = ClassDesc.ofDescriptor(Gen.Res.class.descriptorString()); - public final static ClassDesc CD_Yield = ClassDesc.ofDescriptor(Gen.Yield.class.descriptorString()); - public final static ClassDesc CD_Ret = ClassDesc.ofDescriptor(Gen.Ret.class.descriptorString()); - public final static MethodTypeDesc MTD_Res = MethodTypeDesc.of(CD_Res); - public final static MethodTypeDesc MTD_Gen_Obj = MethodTypeDesc.of(CD_Gen, ConstantDescs.CD_Object); - public final static MethodTypeDesc MTD_Gen = MethodTypeDesc.of(CD_Gen); - public static MethodTypeDesc MTD_Obj = MethodTypeDesc.of(ConstantDescs.CD_Object); - - public final ClassDesc CD_this; - public final ClassDesc[] params; - public final MethodTypeDesc MTD_init; - public final int paramSlotOff; - public final ClassModel clm; - - public interface ParamConsumer{ - void consume(String param, int slot, ClassDesc type); - } - - public GeneratorBuilder(ClassModel clm, ClassDesc cd, ClassDesc[] params){ - this.clm = clm; - CD_this = cd; - this.params = params; - MTD_init = MethodTypeDesc.of(ConstantDescs.CD_void, params); - paramSlotOff = Arrays.stream(params).mapToInt(p -> TypeKind.from(p).slotSize()).sum(); - } - - public void params(int slot_start, ParamConsumer consumer){ - int offset = 0; - for (var param : params) { - consumer.consume(PARAM_PREFIX+offset, offset+slot_start, param); - offset += TypeKind.from(param).slotSize(); - } - } - - public void buildGeneratorMethodShim(CodeBuilder cob){ - cob.new_(CD_this).dup(); - params(0, (_, slot, type) -> { - cob.loadLocal(TypeKind.from(type), slot); - }); - cob.invokespecial(CD_this, ConstantDescs.INIT_NAME, MTD_init).areturn(); - } - - public byte[] buildGenerator(CodeModel com){ - return ClassFile.of(ClassFile.StackMapsOption.STACK_MAPS_WHEN_REQUIRED, ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).build(CD_this, clb -> { - - clm.findAttributes(Attributes.sourceFile()).forEach(clb::with); - clb.with(InnerClassesAttribute.of(InnerClassInfo.of(CD_this, Optional.of(clm.thisClass().asSymbol()), Optional.of(CD_this.displayName().split("\\$")[1]), AccessFlag.PUBLIC, AccessFlag.FINAL, AccessFlag.STATIC))); - clb.with(NestHostAttribute.of(clm.thisClass())); - - clb.withInterfaces(List.of(clb.constantPool().classEntry(CD_Gen))); - // parameter fields - params(0, (param, _, type) -> { - clb.withField(param, type, ClassFile.ACC_PRIVATE); - }); - // fms state - clb.withField(STATE_NAME, ConstantDescs.CD_int, ClassFile.ACC_PRIVATE); - - // constructor - clb.withMethod(ConstantDescs.INIT_NAME, MTD_init, ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> { - cob.aload(0).invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME, ConstantDescs.MTD_void); - params(1, (param, slot, type) -> { - cob.aload(0).loadLocal(TypeKind.from(type), slot).putfield(CD_this, param, type); - }); - cob.return_(); - })); - - // fms method - clb.withMethod("next", MethodTypeDesc.of(CD_Res), ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> { - buildGeneratorNext(clb, cob, com); - })); - }); - } - - private void buildGeneratorNext(ClassBuilder clb, CodeBuilder cob, CodeModel com){ - cob.trying( - tcob -> new StateMachineBuilder(this, clb, tcob, com).generateStateMachine(), - // catch anything set our state to -1 and throw the exception - ctb -> ctb.catchingAll( - blc -> blc.aload(0).loadConstant(-1).putfield(CD_this, STATE_NAME, ConstantDescs.CD_int).athrow() - ) - ).aconst_null().areturn(); - } -} diff --git a/src/generator/runtime/GeneratorClassLoader.java b/src/generator/runtime/GeneratorClassLoader.java index 4c4fbed..4ec63af 100644 --- a/src/generator/runtime/GeneratorClassLoader.java +++ b/src/generator/runtime/GeneratorClassLoader.java @@ -1,21 +1,18 @@ package generator.runtime; import generator.gen.Gen; +import generator.runtime.gen.GenSMBuilder; import java.io.IOException; import java.lang.classfile.*; import java.lang.classfile.attribute.*; import java.lang.classfile.constantpool.ClassEntry; import java.lang.constant.ClassDesc; -import java.lang.constant.ConstantDescs; -import java.lang.reflect.AccessFlag; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.atomic.AtomicReference; public class GeneratorClassLoader extends ClassLoader { - private final HashMap customClazzDefMap = new HashMap<>(); private final HashMap> customClazzMap = new HashMap<>(); public GeneratorClassLoader(ClassLoader parent) { @@ -23,13 +20,12 @@ public class GeneratorClassLoader extends ClassLoader { } void add(String name, byte[] def){ - try { - Files.createDirectories(Path.of("out/modified/generators/" + name.replace(".", "/")).getParent()); - Files.write(Path.of("out/modified/generators/" + name.replace(".", "/") + ".class"), def); - } catch (IOException e) { - throw new RuntimeException(e); - } - customClazzDefMap.put(name, def); + try { + Files.createDirectories(Path.of("out/modified/generators/" + name.replace(".", "/")).getParent()); + Files.write(Path.of("out/modified/generators/" + name.replace(".", "/") + ".class"), def); + } catch (IOException e) { + throw new RuntimeException(e); + } customClazzMap.put(name, defineClass(name, def, 0, def.length)); } @@ -68,8 +64,8 @@ public class GeneratorClassLoader extends ClassLoader { cb.with(mem); } else{ var gcd = generatorMethod(cb, mem, clm); - innerCl.add(InnerClassInfo.of(gcd, Optional.of(clm.thisClass().asSymbol()), Optional.of(gcd.displayName()), AccessFlag.PUBLIC, AccessFlag.FINAL, AccessFlag.STATIC)); - nestMem.add(ClassDesc.of(gcd.displayName())); +// innerCl.add(InnerClassInfo.of(gcd, Optional.of(clm.thisClass().asSymbol()), Optional.of(gcd.displayName()), AccessFlag.PUBLIC, AccessFlag.FINAL, AccessFlag.STATIC)); +// nestMem.add(ClassDesc.of(gcd.displayName())); } } else if (ce instanceof Attribute e){ @@ -85,29 +81,13 @@ public class GeneratorClassLoader extends ClassLoader { }); } - private ClassDesc generatorMethod(ClassBuilder cb, MethodModel mem, ClassModel clm) { - - AtomicReference gcd = new AtomicReference<>(); - - cb.withMethod(mem.methodName(), mem.methodType(), mem.flags().flagsMask(), mb -> { - for (var me : mem) { - if (me instanceof CodeModel com) { - var mts = mem.methodTypeSymbol(); - mts = mts.changeReturnType(ConstantDescs.CD_void); - if (!mem.flags().has(AccessFlag.STATIC)) { - mts = mts.insertParameterTypes(0, clm.thisClass().asSymbol()); - } - var name = clm.thisClass().name().stringValue()+"$Gen_" + mem.methodName().stringValue() + "_" + customClazzDefMap.size(); - - gcd.set(ClassDesc.of(clm.thisClass().asSymbol().packageName(), name)); - var gb = new GeneratorBuilder(clm, gcd.get(), mts.parameterArray()); - - mb.withCode(gb::buildGeneratorMethodShim); - add(gb.CD_this.displayName(), gb.buildGenerator(com)); - } else mb.with(me); - } + private ClassDesc generatorMethod(ClassBuilder cb, MethodModel src_mem, ClassModel src_clm) { + var com = src_mem.code().get(); + var gb = new GenSMBuilder(src_clm, src_mem, com); + add(gb.CD_this.displayName(), gb.buildStateMachine()); + cb.withMethod(src_mem.methodName(), src_mem.methodType(), src_mem.flags().flagsMask(), mb -> { + mb.withCode(gb::buildSourceMethodShim); }); - - return gcd.get(); + return gb.CD_this; } } diff --git a/src/generator/runtime/LocalTracker.java b/src/generator/runtime/LocalTracker.java index d1cc038..c68ed7e 100644 --- a/src/generator/runtime/LocalTracker.java +++ b/src/generator/runtime/LocalTracker.java @@ -8,7 +8,7 @@ import java.util.HashMap; import static java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo.TOP; -class LocalTracker { +public class LocalTracker { record LocalStore(String name, ClassDesc cd) { } @@ -23,7 +23,7 @@ class LocalTracker { LocalTracker(StateMachineBuilder smb, CodeModel com) { int offset = 0; - for (var param : smb.gb.params) { + for (var param : smb.params) { parameter_map.put(offset, param); offset += TypeKind.from(param).slotSize(); } @@ -32,8 +32,8 @@ class LocalTracker { var entries = new ArrayList(); for (var smfi : attr.entries()) { var locals = new ArrayList<>(smfi.locals()); - for (int i = 0; i < smb.gb.params.length; i++) locals.removeFirst(); - locals.addFirst(StackMapFrameInfo.ObjectVerificationTypeInfo.of(smb.gb.CD_this)); + for (int i = 0; i < smb.params.length; i++) locals.removeFirst(); + locals.addFirst(StackMapFrameInfo.ObjectVerificationTypeInfo.of(smb.CD_this)); entries.add(StackMapFrameInfo.of(smfi.target(), locals, smfi.stack())); stackMapFrames.put(smfi.target(), entries.getLast()); } @@ -57,7 +57,7 @@ class LocalTracker { break; } if (name == null) { - name = GeneratorBuilder.LOCAL_PREFIX + localStore.size(); + name = StateMachineBuilder.LOCAL_PREFIX + localStore.size(); localStore.add(new LocalStore(name, desc)); } saved.add(new Saved(slot, name, desc)); diff --git a/src/generator/runtime/ParamConsumer.java b/src/generator/runtime/ParamConsumer.java new file mode 100644 index 0000000..8f1548b --- /dev/null +++ b/src/generator/runtime/ParamConsumer.java @@ -0,0 +1,7 @@ +package generator.runtime; + +import java.lang.constant.ClassDesc; + +public interface ParamConsumer { + void consume(String param, int slot, ClassDesc type); +} diff --git a/src/generator/runtime/ReplacementKind.java b/src/generator/runtime/ReplacementKind.java new file mode 100644 index 0000000..9e1d461 --- /dev/null +++ b/src/generator/runtime/ReplacementKind.java @@ -0,0 +1,7 @@ +package generator.runtime; + +public enum ReplacementKind { + ImmediateReplacingPop, + Immediate, + ReplacingNextReturn, +} diff --git a/src/generator/runtime/SpecialMethod.java b/src/generator/runtime/SpecialMethod.java new file mode 100644 index 0000000..0338acf --- /dev/null +++ b/src/generator/runtime/SpecialMethod.java @@ -0,0 +1,7 @@ +package generator.runtime; + +import java.lang.constant.ClassDesc; +import java.lang.constant.MethodTypeDesc; + +public record SpecialMethod(ClassDesc owner, String name, MethodTypeDesc desc) { +} diff --git a/src/generator/runtime/SpecialMethodHandler.java b/src/generator/runtime/SpecialMethodHandler.java new file mode 100644 index 0000000..988029e --- /dev/null +++ b/src/generator/runtime/SpecialMethodHandler.java @@ -0,0 +1,13 @@ +package generator.runtime; + +import java.lang.classfile.CodeBuilder; + +public interface SpecialMethodHandler { + void handle(StateMachineBuilder smb, CodeBuilder cob); + + default boolean removeCall() { + return true; + } + + ReplacementKind replacementKind(); +} diff --git a/src/generator/runtime/StateMachineBuilder.java b/src/generator/runtime/StateMachineBuilder.java index 85f8c93..35fc336 100644 --- a/src/generator/runtime/StateMachineBuilder.java +++ b/src/generator/runtime/StateMachineBuilder.java @@ -1,162 +1,161 @@ package generator.runtime; +import generator.gen.Gen; + import java.lang.classfile.*; +import java.lang.classfile.attribute.InnerClassInfo; +import java.lang.classfile.attribute.InnerClassesAttribute; +import java.lang.classfile.attribute.NestHostAttribute; import java.lang.classfile.instruction.*; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; import java.lang.constant.MethodTypeDesc; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.function.Function; +import java.lang.reflect.AccessFlag; +import java.util.*; +import java.util.function.BiFunction; -public class StateMachineBuilder { - final ClassBuilder clb; - final CodeBuilder cob; - final CodeModel com; - final GeneratorBuilder gb; - final LocalTracker lt; +public abstract class StateMachineBuilder { + public final static String PARAM_PREFIX = "param_"; + public final static String LOCAL_PREFIX = "local_"; + public final static String STATE_NAME = "state"; - private HashMap> smmap = new HashMap<>(); - private boolean ignore_next_pop = false; - final ArrayList stateSwitchCases = new ArrayList<>(); - final Label invalidState; + private static int sequence; + public final ClassDesc CD_this; + public final ClassDesc[] params; + public final MethodTypeDesc MTD_init; + public final int paramSlotOff; - public enum HandlerRan{ - ImmediateRemovePop, - Immediate, - ReplacingNextReturn, - } - public interface SpecialMethodHandler { - void handle(StateMachineBuilder smb); - default boolean removeCall(){return true;} - default HandlerRan handlerRan(){return HandlerRan.ImmediateRemovePop;} + public final ClassModel src_clm; + public final MethodModel src_mem; + public final CodeModel src_com; + + public final LocalTracker lt; + + protected HashMap> smmap = new HashMap<>(); + + private final ArrayList stateSwitchCases = new ArrayList<>(); + + protected final String uniqueName(){ + return sequence+++""; } - public record SpecialMethod(ClassDesc owner, String name, MethodTypeDesc desc) { - } - - static class YieldHandler implements SpecialMethodHandler { - final int resume_state; - final Label resume_label; - final boolean is_void; - - public YieldHandler(StateMachineBuilder smb, boolean is_void) { - resume_state = smb.add_state(resume_label = smb.cob.newLabel()); - this.is_void = is_void; - } - - @Override - public void handle(StateMachineBuilder smb) { - if(is_void)smb.cob.aconst_null(); - - smb.lt.savingLocals(smb.gb.CD_this, smb.cob, () -> { - smb.cob.aload(0).loadConstant(resume_state).putfield(smb.gb.CD_this, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()) - .new_(GeneratorBuilder.CD_Yield) - .dup_x1() - .swap() - .invokespecial(GeneratorBuilder.CD_Yield, ConstantDescs.INIT_NAME, MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Object)) - .areturn(); - smb.cob.labelBinding(resume_label); - }); - smb.ignore_next_pop = true; + public void params(int slot_start, ParamConsumer consumer){ + int offset = 0; + for (var param : params) { + consumer.consume(PARAM_PREFIX+offset, offset+slot_start, param); + offset += TypeKind.from(param).slotSize(); } } - static class RetHandler implements SpecialMethodHandler { - final boolean is_void; + public StateMachineBuilder(ClassModel src_clm, MethodModel src_mem, CodeModel src_com){ + this.src_clm = src_clm; + this.src_mem = src_mem; + this.src_com = src_com; - public RetHandler(boolean is_void) { - this.is_void = is_void; + var mts = src_mem.methodTypeSymbol(); + mts = mts.changeReturnType(ConstantDescs.CD_void); + if (!src_mem.flags().has(AccessFlag.STATIC)) { + mts = mts.insertParameterTypes(0, src_clm.thisClass().asSymbol()); } + var name = src_clm.thisClass().name().stringValue() + "$" + src_mem.methodName().stringValue() + "$" + uniqueName(); - public HandlerRan handlerRan(){return HandlerRan.ReplacingNextReturn;} + this.CD_this = ClassDesc.of(src_clm.thisClass().asSymbol().packageName(), name); + this.params = mts.parameterArray(); + this.MTD_init = MethodTypeDesc.of(ConstantDescs.CD_void, params); + this.paramSlotOff = Arrays.stream(params).mapToInt(p -> TypeKind.from(p).slotSize()).sum(); - @Override - public void handle(StateMachineBuilder smb) { - if(is_void)smb.cob.aconst_null(); - - smb.cob.aload(0).loadConstant(-1).putfield(smb.gb.CD_this, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()) - .new_(GeneratorBuilder.CD_Ret) - .dup_x1() - .swap() - .invokespecial(GeneratorBuilder.CD_Ret, ConstantDescs.INIT_NAME, MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Object)) - .areturn(); - } + this.lt = new LocalTracker(this, src_com); } - static class AwaitHandler implements SpecialMethodHandler{ - final int yield_state; - final Label yield_label; - - public AwaitHandler(StateMachineBuilder smb) { - yield_state = smb.add_state(yield_label = smb.cob.newLabel()); - } - - public HandlerRan handlerRan(){return HandlerRan.Immediate;} - - @Override - public void handle(StateMachineBuilder smb) { - smb.cob.aload(0).loadConstant(yield_state).putfield(smb.gb.CD_this, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()); - var start = smb.cob.newBoundLabel(); - smb.cob.dup().dup() - .invokeinterface(GeneratorBuilder.CD_Gen, "next", MethodTypeDesc.of(GeneratorBuilder.CD_Res)).dup() - .instanceOf(GeneratorBuilder.CD_Ret); - smb.cob.ifThenElse(bcb -> { - bcb.checkcast(GeneratorBuilder.CD_Ret).invokevirtual(GeneratorBuilder.CD_Ret, "v", MethodTypeDesc.of(ConstantDescs.CD_Object)).swap().pop(); - }, bcb -> { - smb.lt.savingLocals(smb.gb.CD_this, bcb, () -> { - bcb.swap().loadLocal(TypeKind.from(smb.gb.CD_this), 0).swap().putfield(smb.gb.CD_this, "meow", GeneratorBuilder.CD_Gen); - bcb.areturn().labelBinding(yield_label); - bcb.loadLocal(TypeKind.from(smb.gb.CD_this), 0).getfield(smb.gb.CD_this, "meow", GeneratorBuilder.CD_Gen); - }); - bcb.goto_(start); - }); - - } - } - - StateMachineBuilder(GeneratorBuilder gb, ClassBuilder clb, CodeBuilder cob, CodeModel com) { - this.gb = gb; - this.clb = clb; - this.cob = cob; - this.com = com; - this.lt = new LocalTracker(this, com); - invalidState = cob.newLabel(); - - smmap.put(new SpecialMethod(GeneratorBuilder.CD_Gen, "yield", GeneratorBuilder.MTD_Gen_Obj),smb -> new YieldHandler(smb, false)); - smmap.put(new SpecialMethod(GeneratorBuilder.CD_Gen, "yield", GeneratorBuilder.MTD_Gen),smb -> new YieldHandler(smb, true)); - smmap.put(new SpecialMethod(GeneratorBuilder.CD_Gen, "ret", GeneratorBuilder.MTD_Gen_Obj),_ -> new RetHandler(false)); - smmap.put(new SpecialMethod(GeneratorBuilder.CD_Gen, "ret", GeneratorBuilder.MTD_Gen),_ -> new RetHandler(true)); - smmap.put(new SpecialMethod(GeneratorBuilder.CD_Gen, "await", GeneratorBuilder.MTD_Obj), AwaitHandler::new); - } - - int add_state(Label label) { + public int add_state(Label label) { stateSwitchCases.add(SwitchCase.of(stateSwitchCases.size(), label)); return stateSwitchCases.size() - 1; } - public void generateStateMachine() { + public void buildSourceMethodShim(CodeBuilder cob){ + cob.new_(CD_this).dup(); + params(0, (_, slot, type) -> { + cob.loadLocal(TypeKind.from(type), slot); + }); + cob.invokespecial(CD_this, ConstantDescs.INIT_NAME, MTD_init).areturn(); + } + + public boolean shouldBeInnerClass(){ + return false; + } + + public byte[] buildStateMachine(){ + return ClassFile.of(ClassFile.StackMapsOption.STACK_MAPS_WHEN_REQUIRED, ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).build(CD_this, clb -> { + + if(shouldBeInnerClass()){ + src_clm.findAttributes(Attributes.sourceFile()).forEach(clb::with); + clb.with(InnerClassesAttribute.of(InnerClassInfo.of(CD_this, Optional.of(src_clm.thisClass().asSymbol()), Optional.of(CD_this.displayName().split("\\$")[1]), AccessFlag.PUBLIC, AccessFlag.FINAL, AccessFlag.STATIC))); + clb.with(NestHostAttribute.of(src_clm.thisClass())); + } + + // parameter fields + params(0, (param, _, type) -> { + clb.withField(param, type, ClassFile.ACC_PRIVATE); + }); + + clb.withField(STATE_NAME, ConstantDescs.CD_int, ClassFile.ACC_PRIVATE); + + // constructor + clb.withMethod(ConstantDescs.INIT_NAME, MTD_init, ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> { + cob.aload(0).invokespecial(ConstantDescs.CD_Object, ConstantDescs.INIT_NAME, ConstantDescs.MTD_void); + params(1, (param, slot, type) -> { + cob.aload(0).loadLocal(TypeKind.from(type), slot).putfield(CD_this, param, type); + }); + cob.return_(); + })); + + buildStateMachineMethod(clb); + }); + } + + protected abstract void buildStateMachineMethod(ClassBuilder clb); + + public void buildStateMachineMethodCode(ClassBuilder clb, CodeBuilder cob){ + cob.trying( + tcob -> buildStateMachineCode(clb, tcob), + // catch anything set our state to -1 and throw the exception + ctb -> ctb.catchingAll( + blc -> + blc.aload(0).loadConstant(-1).putfield(CD_this, STATE_NAME, ConstantDescs.CD_int) + .new_(ClassDesc.ofDescriptor(RuntimeException.class.descriptorString())) + .dup_x1() + .swap() + .invokespecial(ClassDesc.ofDescriptor(RuntimeException.class.descriptorString()), ConstantDescs.INIT_NAME, MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Throwable)) + .athrow() + ) + ).aconst_null().areturn(); + } + + public void buildStateMachineCode(ClassBuilder clb, CodeBuilder cob) { + boolean ignore_next_pop = false; + + var invalidState = cob.newLabel(); var start_label = cob.newLabel(); add_state(start_label); var handlers = new ArrayList(); - for (CodeElement coe : com) { + for (CodeElement coe : src_com) { if (coe instanceof InvokeInstruction is){ var handler = smmap.get(new SpecialMethod(is.owner().asSymbol(), is.name().stringValue(), is.typeSymbol())); if(handler != null) - handlers.add(handler.apply(this)); + handlers.add(handler.apply(this, cob)); } } - cob.aload(0).getfield(gb.CD_this, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()).lookupswitch(invalidState, stateSwitchCases); + cob.aload(0).getfield(CD_this, STATE_NAME, TypeKind.INT.upperBound()).lookupswitch(invalidState, stateSwitchCases); var start = cob.startLabel(); var end = cob.newLabel(); - cob.localVariable(0, "this", gb.CD_this, start, end); + cob.localVariable(0, "this", CD_this, start, end); SpecialMethodHandler currentHandler = null; cob.labelBinding(start_label); - for (CodeElement coe : com) { + for (CodeElement coe : src_com) { if (coe instanceof Instruction i) { if (ignore_next_pop) if (i.opcode() == Opcode.POP) { @@ -164,8 +163,8 @@ public class StateMachineBuilder { continue; }else throw new RuntimeException("Expected Pop Instruction"); if (i.opcode() == Opcode.ARETURN){ - if (currentHandler !=null && currentHandler.handlerRan() == HandlerRan.ReplacingNextReturn){ - currentHandler.handle(this); + if (currentHandler !=null && currentHandler.replacementKind() == ReplacementKind.ReplacingNextReturn){ + currentHandler.handle(this, cob); currentHandler = null; continue; } @@ -179,9 +178,9 @@ public class StateMachineBuilder { if(currentHandler!=null)throw new RuntimeException("Multiple method handlers at once not supported"); var handler = handlers.removeFirst(); if(!handler.removeCall()) cob.with(coe); - if(handler.handlerRan() == HandlerRan.Immediate) handler.handle(this); - else if(handler.handlerRan() == HandlerRan.ImmediateRemovePop) { - handler.handle(this); + if(handler.replacementKind() == ReplacementKind.Immediate) handler.handle(this, cob); + else if(handler.replacementKind() == ReplacementKind.ImmediateReplacingPop) { + handler.handle(this, cob); ignore_next_pop = true; }else currentHandler = handler; @@ -189,35 +188,33 @@ public class StateMachineBuilder { } } -// System.out.println(coe); - switch (coe) { // locals which were once function parameters can be ignored - case LocalVariable lv when lv.slot() < gb.paramSlotOff -> { + case LocalVariable lv when lv.slot() < paramSlotOff -> { } case LocalVariable lv -> - cob.localVariable(lv.slot() - gb.paramSlotOff + 1, lv.name(), lv.type(), lv.startScope(), lv.endScope()); + cob.localVariable(lv.slot() - paramSlotOff + 1, lv.name(), lv.type(), lv.startScope(), lv.endScope()); // increment indexes into the stack - case IncrementInstruction ii when ii.slot() < gb.paramSlotOff -> - cob.aload(0).dup().getfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int) + case IncrementInstruction ii when ii.slot() < paramSlotOff -> + cob.aload(0).dup().getfield(CD_this, PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int) .loadConstant(ii.constant()).iadd() - .putfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int); - case IncrementInstruction ii -> cob.iinc(ii.slot() - gb.paramSlotOff + 1, ii.constant()); + .putfield(CD_this, PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int); + case IncrementInstruction ii -> cob.iinc(ii.slot() - paramSlotOff + 1, ii.constant()); // convert local function parameters to class fields and offset regular locals - case LoadInstruction li when li.slot() < gb.paramSlotOff -> - cob.aload(0).getfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + li.slot(), lt.paramType(li.slot())); - case LoadInstruction li -> cob.loadLocal(li.typeKind(), li.slot() - gb.paramSlotOff + 1); + case LoadInstruction li when li.slot() < paramSlotOff -> + cob.aload(0).getfield(CD_this, PARAM_PREFIX + li.slot(), lt.paramType(li.slot())); + case LoadInstruction li -> cob.loadLocal(li.typeKind(), li.slot() - paramSlotOff + 1); // convert local function parameters to class fields and offset regular locals - case StoreInstruction ls when ls.slot() < gb.paramSlotOff && ls.typeKind().slotSize() == 2 -> - cob.aload(0).dup_x2().pop().putfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + ls.slot(), lt.paramType(ls.slot())); - case StoreInstruction ls when ls.slot() < gb.paramSlotOff -> - cob.aload(0).swap().putfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + ls.slot(), lt.paramType(ls.slot())); + case StoreInstruction ls when ls.slot() < paramSlotOff && ls.typeKind().slotSize() == 2 -> + cob.aload(0).dup_x2().pop().putfield(CD_this, PARAM_PREFIX + ls.slot(), lt.paramType(ls.slot())); + case StoreInstruction ls when ls.slot() < paramSlotOff -> + cob.aload(0).swap().putfield(CD_this, PARAM_PREFIX + ls.slot(), lt.paramType(ls.slot())); case StoreInstruction ls -> { - lt.trackLocal(ls.slot() - gb.paramSlotOff + 1, ls.typeKind()); - cob.storeLocal(ls.typeKind(), ls.slot() - gb.paramSlotOff + 1); + lt.trackLocal(ls.slot() - paramSlotOff + 1, ls.typeKind()); + cob.storeLocal(ls.typeKind(), ls.slot() - paramSlotOff + 1); } default -> cob.with(coe); @@ -230,6 +227,5 @@ public class StateMachineBuilder { cob.labelBinding(end); lt.createLocalStoreFields(clb); - clb.withField("meow", GeneratorBuilder.CD_Gen, ClassFile.ACC_PRIVATE); } } diff --git a/src/generator/runtime/future/FutureSMBuilder.java b/src/generator/runtime/future/FutureSMBuilder.java new file mode 100644 index 0000000..52ee690 --- /dev/null +++ b/src/generator/runtime/future/FutureSMBuilder.java @@ -0,0 +1,70 @@ +package generator.runtime.future; + +import generator.gen.Gen; +import generator.runtime.ReplacementKind; +import generator.runtime.SpecialMethod; +import generator.runtime.SpecialMethodHandler; +import generator.runtime.StateMachineBuilder; + +import java.lang.classfile.*; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.util.List; + +public class FutureSMBuilder extends StateMachineBuilder { + + public final static ClassDesc CD_Gen = ClassDesc.ofDescriptor(Gen.class.descriptorString()); + public final static ClassDesc CD_Res = ClassDesc.ofDescriptor(Gen.Res.class.descriptorString()); + public final static ClassDesc CD_Yield = ClassDesc.ofDescriptor(Gen.Yield.class.descriptorString()); + public final static ClassDesc CD_Ret = ClassDesc.ofDescriptor(Gen.Ret.class.descriptorString()); + public final static MethodTypeDesc MTD_Res = MethodTypeDesc.of(CD_Res); + public final static MethodTypeDesc MTD_Gen_Obj = MethodTypeDesc.of(CD_Gen, ConstantDescs.CD_Object); + public final static MethodTypeDesc MTD_Gen = MethodTypeDesc.of(CD_Gen); + public final static MethodTypeDesc MTD_Obj = MethodTypeDesc.of(ConstantDescs.CD_Object); + + static class AwaitHandler implements SpecialMethodHandler{ + final int yield_state; + final Label yield_label; + + public AwaitHandler(StateMachineBuilder smb, CodeBuilder cob) { + yield_state = smb.add_state(yield_label = cob.newLabel()); + } + + public ReplacementKind replacementKind(){return ReplacementKind.Immediate;} + + @Override + public void handle(StateMachineBuilder smb, CodeBuilder cob) { + cob.aload(0).loadConstant(yield_state).putfield(smb.CD_this, STATE_NAME, TypeKind.INT.upperBound()); + var start = cob.newBoundLabel(); + cob.dup().dup() + .invokeinterface(CD_Gen, "next", MethodTypeDesc.of(CD_Res)).dup() + .instanceOf(CD_Ret); + cob.ifThenElse(bcb -> { + bcb.checkcast(CD_Ret).invokevirtual(CD_Ret, "v", MethodTypeDesc.of(ConstantDescs.CD_Object)).swap().pop(); + }, bcb -> { + smb.lt.savingLocals(smb.CD_this, bcb, () -> { + bcb.swap().loadLocal(TypeKind.from(smb.CD_this), 0).swap().putfield(smb.CD_this, "meow", CD_Gen); + bcb.areturn().labelBinding(yield_label); + bcb.loadLocal(TypeKind.from(smb.CD_this), 0).getfield(smb.CD_this, "meow", CD_Gen); + }); + bcb.goto_(start); + }); + + } + } + + @Override + protected void buildStateMachineMethod(ClassBuilder clb){ + clb.withInterfaces(List.of(clb.constantPool().classEntry(CD_Gen))); + clb.withMethod("next", MethodTypeDesc.of(CD_Res), ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> { + buildStateMachineMethodCode(clb, cob); + })); + clb.withField("meow", CD_Gen, ClassFile.ACC_PRIVATE); + } + + public FutureSMBuilder(ClassModel src_clm, MethodModel src_mem, CodeModel src_com) { + super(src_clm, src_mem, src_com); + smmap.put(new SpecialMethod(CD_Gen, "await", MTD_Obj), AwaitHandler::new); + } +} diff --git a/src/generator/runtime/gen/GenSMBuilder.java b/src/generator/runtime/gen/GenSMBuilder.java new file mode 100644 index 0000000..9f2cf66 --- /dev/null +++ b/src/generator/runtime/gen/GenSMBuilder.java @@ -0,0 +1,91 @@ +package generator.runtime.gen; + +import generator.gen.Gen; +import generator.runtime.ReplacementKind; +import generator.runtime.SpecialMethod; +import generator.runtime.SpecialMethodHandler; +import generator.runtime.StateMachineBuilder; + +import java.lang.classfile.*; +import java.lang.constant.ClassDesc; +import java.lang.constant.ConstantDescs; +import java.lang.constant.MethodTypeDesc; +import java.util.List; + +public class GenSMBuilder extends StateMachineBuilder { + + public final static ClassDesc CD_Gen = ClassDesc.ofDescriptor(Gen.class.descriptorString()); + public final static ClassDesc CD_Res = ClassDesc.ofDescriptor(Gen.Res.class.descriptorString()); + public final static ClassDesc CD_Yield = ClassDesc.ofDescriptor(Gen.Yield.class.descriptorString()); + public final static ClassDesc CD_Ret = ClassDesc.ofDescriptor(Gen.Ret.class.descriptorString()); + public final static MethodTypeDesc MTD_Res = MethodTypeDesc.of(CD_Res); + public final static MethodTypeDesc MTD_Gen_Obj = MethodTypeDesc.of(CD_Gen, ConstantDescs.CD_Object); + public final static MethodTypeDesc MTD_Gen = MethodTypeDesc.of(CD_Gen); + public final static MethodTypeDesc MTD_Obj = MethodTypeDesc.of(ConstantDescs.CD_Object); + + static class YieldHandler implements SpecialMethodHandler { + final int resume_state; + final Label resume_label; + final boolean is_void; + + public YieldHandler(StateMachineBuilder smb, CodeBuilder cob, boolean is_void) { + resume_state = smb.add_state(resume_label = cob.newLabel()); + this.is_void = is_void; + } + + public ReplacementKind replacementKind(){return ReplacementKind.ImmediateReplacingPop;} + + @Override + public void handle(StateMachineBuilder smb, CodeBuilder cob) { + if(is_void)cob.aconst_null(); + + smb.lt.savingLocals(smb.CD_this, cob, () -> { + cob.aload(0).loadConstant(resume_state).putfield(smb.CD_this, STATE_NAME, TypeKind.INT.upperBound()) + .new_(CD_Yield) + .dup_x1() + .swap() + .invokespecial(CD_Yield, ConstantDescs.INIT_NAME, MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Object)) + .areturn(); + cob.labelBinding(resume_label); + }); + } + } + + static class RetHandler implements SpecialMethodHandler { + final boolean is_void; + + public RetHandler(boolean is_void) { + this.is_void = is_void; + } + + public ReplacementKind replacementKind(){return ReplacementKind.ReplacingNextReturn;} + + @Override + public void handle(StateMachineBuilder smb, CodeBuilder cob) { + if(is_void)cob.aconst_null(); + + cob.aload(0).loadConstant(-1).putfield(smb.CD_this, STATE_NAME, TypeKind.INT.upperBound()) + .new_(CD_Ret) + .dup_x1() + .swap() + .invokespecial(CD_Ret, ConstantDescs.INIT_NAME, MethodTypeDesc.of(ConstantDescs.CD_void, ConstantDescs.CD_Object)) + .areturn(); + } + } + + public GenSMBuilder(ClassModel src_clm, MethodModel src_mem, CodeModel src_com) { + super(src_clm, src_mem, src_com); + smmap.put(new SpecialMethod(CD_Gen, "yield", MTD_Gen_Obj),(smb, cob) -> new YieldHandler(smb, cob, false)); + smmap.put(new SpecialMethod(CD_Gen, "yield", MTD_Gen),(smb, cob) -> new YieldHandler(smb, cob, true)); + smmap.put(new SpecialMethod(CD_Gen, "ret", MTD_Gen_Obj),(_, _) -> new RetHandler(false)); + smmap.put(new SpecialMethod(CD_Gen, "ret", MTD_Gen),(_, _) -> new RetHandler(true)); + } + + @Override + protected void buildStateMachineMethod(ClassBuilder clb){ + clb.withInterfaces(List.of(clb.constantPool().classEntry(CD_Gen))); + clb.withMethod("next", MethodTypeDesc.of(CD_Res), ClassFile.ACC_PUBLIC, mb -> mb.withCode(cob -> { + buildStateMachineMethodCode(clb, cob); + })); + } +}