From 3cac1da3718def1c823a5d876082e652e04d64f0 Mon Sep 17 00:00:00 2001 From: Parker TenBroeck <51721964+ParkerTenBroeck@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:48:04 -0400 Subject: [PATCH] made class behave like a static inner class, write all modified classes to directory --- src/Main.java | 4 +- src/generator/runtime/GeneratorBuilder.java | 29 ++++--- .../runtime/GeneratorClassLoader.java | 83 +++++++++++-------- src/generator/runtime/LocalTracker.java | 2 +- .../runtime/StateMachineBuilder.java | 30 +++---- 5 files changed, 87 insertions(+), 61 deletions(-) diff --git a/src/Main.java b/src/Main.java index 61f29d0..4f46b88 100644 --- a/src/Main.java +++ b/src/Main.java @@ -8,8 +8,8 @@ public class Main implements Runnable { @Override public void run() { - await(); -// lexer(); +// await(); + lexer(); } void await(){ diff --git a/src/generator/runtime/GeneratorBuilder.java b/src/generator/runtime/GeneratorBuilder.java index b257ed6..0938771 100644 --- a/src/generator/runtime/GeneratorBuilder.java +++ b/src/generator/runtime/GeneratorBuilder.java @@ -3,9 +3,13 @@ package generator.runtime; import generator.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 { @@ -23,19 +27,19 @@ public class GeneratorBuilder { public final static MethodTypeDesc MTD_Gen = MethodTypeDesc.of(CD_Gen); public static MethodTypeDesc MTD_Obj = MethodTypeDesc.of(ConstantDescs.CD_Object); - public final String name; - public final ClassDesc CD_this_gen; + 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(String name, ClassDesc[] params){ - this.name = name; - CD_this_gen = ClassDesc.of(name); + 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(); @@ -50,15 +54,20 @@ public class GeneratorBuilder { } public void buildGeneratorMethodShim(CodeBuilder cob){ - cob.new_(CD_this_gen).dup(); + cob.new_(CD_this).dup(); params(0, (_, slot, type) -> { cob.loadLocal(TypeKind.from(type), slot); }); - cob.invokespecial(CD_this_gen, ConstantDescs.INIT_NAME, MTD_init).areturn(); + 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.DebugElementsOption.PASS_DEBUG, ClassFile.LineNumbersOption.PASS_LINE_NUMBERS, ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).build(CD_this_gen, clb -> { + 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) -> { @@ -71,7 +80,7 @@ public class GeneratorBuilder { 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_gen, param, type); + cob.aload(0).loadLocal(TypeKind.from(type), slot).putfield(CD_this, param, type); }); cob.return_(); })); @@ -88,7 +97,7 @@ public class GeneratorBuilder { 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_gen, STATE_NAME, ConstantDescs.CD_int).athrow() + 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 69f2b11..4556641 100644 --- a/src/generator/runtime/GeneratorClassLoader.java +++ b/src/generator/runtime/GeneratorClassLoader.java @@ -4,22 +4,16 @@ import generator.Fun; import generator.Gen; import java.io.IOException; -import java.io.PrintStream; import java.lang.classfile.*; -import java.lang.classfile.attribute.StackMapFrameInfo; -import java.lang.classfile.instruction.*; +import java.lang.classfile.attribute.*; +import java.lang.classfile.constantpool.ClassEntry; import java.lang.constant.ClassDesc; import java.lang.constant.ConstantDescs; -import java.lang.constant.MethodTypeDesc; import java.lang.reflect.AccessFlag; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; - -import static java.lang.classfile.attribute.StackMapFrameInfo.SimpleVerificationTypeInfo.TOP; +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; public class GeneratorClassLoader extends ClassLoader { private final HashMap customClazzDefMap = new HashMap<>(); @@ -29,12 +23,19 @@ public class GeneratorClassLoader extends ClassLoader { super(parent); } + 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); + customClazzMap.put(name, defineClass(name, def, 0, def.length)); + } + @Override public Class loadClass(String name) throws ClassNotFoundException { -// if(customClazzDefMap.containsKey(name)) -// return super.loadClass(name); - if (customClazzDefMap.get(name) instanceof byte[] bytes) - customClazzMap.put(name, defineClass(name, bytes, 0, bytes.length)); if (customClazzMap.get(name) instanceof Class clazz) return clazz; if (name.startsWith("java")) @@ -43,9 +44,7 @@ public class GeneratorClassLoader extends ClassLoader { var p = "/" + name.replace('.', '/') + ".class"; try (var stream = Fun.class.getResourceAsStream(p)) { var bytes = Objects.requireNonNull(stream).readAllBytes(); - bytes = searchForGenerators(bytes); - customClazzDefMap.put(name, bytes); - customClazzMap.put(name, defineClass(name, bytes, 0, bytes.length)); + add(name, searchForGenerators(bytes)); return customClazzMap.get(name); } catch (IOException e) { throw new ClassNotFoundException(name, e); @@ -53,24 +52,44 @@ public class GeneratorClassLoader extends ClassLoader { } public byte[] searchForGenerators(byte[] in) { - var clm = ClassFile.of(ClassFile.DebugElementsOption.PASS_DEBUG, ClassFile.LineNumbersOption.PASS_LINE_NUMBERS, ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).parse(in); + var clm = ClassFile.of(ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).parse(in); var isGen = clm.thisClass().asSymbol().descriptorString().equals(Gen.class.descriptorString()); - return ClassFile.of(ClassFile.DebugElementsOption.PASS_DEBUG, ClassFile.LineNumbersOption.PASS_LINE_NUMBERS, ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).build(clm.thisClass().asSymbol(), cb -> { + + + var nestMem = new ArrayList(); + var innerCl = new ArrayList(); + clm.findAttributes(Attributes.nestMembers()).forEach(i -> nestMem.addAll(i.nestMembers().stream().map(ClassEntry::asSymbol).toList())); + clm.findAttributes(Attributes.innerClasses()).forEach(i -> innerCl.addAll(i.classes())); + + return ClassFile.of(ClassFile.AttributesProcessingOption.PASS_ALL_ATTRIBUTES).build(clm.thisClass().asSymbol(), cb -> { for (var ce : clm) { if (ce instanceof MethodModel mem && !isGen) { var methodRetGen = mem.methodTypeSymbol().returnType().descriptorString().equals(Gen.class.descriptorString()); if (!methodRetGen) { cb.with(mem); } else{ - generatorMethod(cb, mem, clm); + 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())); } - } else - cb.with(ce); + } + else if (ce instanceof Attribute e){ + if (e.attributeMapper() != Attributes.nestMembers() && e.attributeMapper() != Attributes.innerClasses()) + cb.with(ce); + } + else cb.with(ce); } + if(!innerCl.isEmpty()) + cb.with(InnerClassesAttribute.of(innerCl)); + if(!nestMem.isEmpty()) + cb.with(NestMembersAttribute.ofSymbols(nestMem)); }); } - private void generatorMethod(ClassBuilder cb, MethodModel mem, ClassModel clm) { + 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) { @@ -79,21 +98,17 @@ public class GeneratorClassLoader extends ClassLoader { if (!mem.flags().has(AccessFlag.STATIC)) { mts = mts.insertParameterTypes(0, clm.thisClass().asSymbol()); } - var name = "Gen_" + clm.thisClass().name().stringValue() + "_" + mem.methodName().stringValue() + "_" + customClazzDefMap.size(); - var gb = new GeneratorBuilder(name, mts.parameterArray()); + 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); - addGenerator(gb.CD_this_gen.displayName(), gb.buildGenerator(com)); + add(gb.CD_this.displayName(), gb.buildGenerator(com)); } else mb.with(me); } }); - } - protected void addGenerator(String name, byte[] def){ - try { - Files.write(Path.of("out/production/generators/" + name + ".class"), def); - } catch (IOException e) { - throw new RuntimeException(e); - } - customClazzDefMap.put(name, def); + return gcd.get(); } } diff --git a/src/generator/runtime/LocalTracker.java b/src/generator/runtime/LocalTracker.java index 8e993fd..d1cc038 100644 --- a/src/generator/runtime/LocalTracker.java +++ b/src/generator/runtime/LocalTracker.java @@ -33,7 +33,7 @@ class LocalTracker { 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_gen)); + locals.addFirst(StackMapFrameInfo.ObjectVerificationTypeInfo.of(smb.gb.CD_this)); entries.add(StackMapFrameInfo.of(smfi.target(), locals, smfi.stack())); stackMapFrames.put(smfi.target(), entries.getLast()); } diff --git a/src/generator/runtime/StateMachineBuilder.java b/src/generator/runtime/StateMachineBuilder.java index 53094b2..85f8c93 100644 --- a/src/generator/runtime/StateMachineBuilder.java +++ b/src/generator/runtime/StateMachineBuilder.java @@ -50,8 +50,8 @@ public class StateMachineBuilder { public void handle(StateMachineBuilder smb) { if(is_void)smb.cob.aconst_null(); - smb.lt.savingLocals(smb.gb.CD_this_gen, smb.cob, () -> { - smb.cob.aload(0).loadConstant(resume_state).putfield(smb.gb.CD_this_gen, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()) + 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() @@ -76,7 +76,7 @@ public class StateMachineBuilder { public void handle(StateMachineBuilder smb) { if(is_void)smb.cob.aconst_null(); - smb.cob.aload(0).loadConstant(-1).putfield(smb.gb.CD_this_gen, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()) + smb.cob.aload(0).loadConstant(-1).putfield(smb.gb.CD_this, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()) .new_(GeneratorBuilder.CD_Ret) .dup_x1() .swap() @@ -97,7 +97,7 @@ public class StateMachineBuilder { @Override public void handle(StateMachineBuilder smb) { - smb.cob.aload(0).loadConstant(yield_state).putfield(smb.gb.CD_this_gen, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()); + 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() @@ -105,10 +105,10 @@ public class StateMachineBuilder { 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_gen, bcb, () -> { - bcb.swap().loadLocal(TypeKind.from(smb.gb.CD_this_gen), 0).swap().putfield(smb.gb.CD_this_gen, "meow", GeneratorBuilder.CD_Gen); + 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_gen), 0).getfield(smb.gb.CD_this_gen, "meow", GeneratorBuilder.CD_Gen); + bcb.loadLocal(TypeKind.from(smb.gb.CD_this), 0).getfield(smb.gb.CD_this, "meow", GeneratorBuilder.CD_Gen); }); bcb.goto_(start); }); @@ -148,10 +148,10 @@ public class StateMachineBuilder { handlers.add(handler.apply(this)); } } - cob.aload(0).getfield(gb.CD_this_gen, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()).lookupswitch(invalidState, stateSwitchCases); + cob.aload(0).getfield(gb.CD_this, GeneratorBuilder.STATE_NAME, TypeKind.INT.upperBound()).lookupswitch(invalidState, stateSwitchCases); var start = cob.startLabel(); var end = cob.newLabel(); - cob.localVariable(0, "this", gb.CD_this_gen, start, end); + cob.localVariable(0, "this", gb.CD_this, start, end); SpecialMethodHandler currentHandler = null; @@ -189,6 +189,8 @@ 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 -> { @@ -198,21 +200,21 @@ public class StateMachineBuilder { // increment indexes into the stack case IncrementInstruction ii when ii.slot() < gb.paramSlotOff -> - cob.aload(0).dup().getfield(gb.CD_this_gen, GeneratorBuilder.PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int) + cob.aload(0).dup().getfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int) .loadConstant(ii.constant()).iadd() - .putfield(gb.CD_this_gen, GeneratorBuilder.PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int); + .putfield(gb.CD_this, GeneratorBuilder.PARAM_PREFIX + ii.slot(), ConstantDescs.CD_int); case IncrementInstruction ii -> cob.iinc(ii.slot() - gb.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_gen, GeneratorBuilder.PARAM_PREFIX + li.slot(), lt.paramType(li.slot())); + 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); // 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_gen, GeneratorBuilder.PARAM_PREFIX + ls.slot(), lt.paramType(ls.slot())); + 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_gen, GeneratorBuilder.PARAM_PREFIX + ls.slot(), lt.paramType(ls.slot())); + cob.aload(0).swap().putfield(gb.CD_this, GeneratorBuilder.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);