Last active
December 8, 2020 14:32
-
-
Save eutro/2b63a8eb0d27f8e72b640469db38967b to your computer and use it in GitHub Desktop.
Day 8 of AoC 2020 but it compiles to Java bytecode.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.*; | |
import java.util.*; | |
import java.util.stream.*; | |
import org.objectweb.asm.*; | |
import org.objectweb.asm.commons.*; | |
import static org.objectweb.asm.Opcodes.*; | |
public class Day8 { | |
public static void main(String[] args) throws Exception { | |
Program.Interpreted program = Program.from(System.in); | |
System.out.println("Part 1:"); | |
try { | |
System.out.println(program.compile().run()); | |
} catch (Program.CyclicProgramException e) { | |
System.out.println(e.getMessage()); | |
} | |
System.out.println("Part 2:"); | |
for (Program.Instruction insn : program.instructions) { | |
if (!insn.flip()) continue; | |
try { | |
System.out.println(program.compile().run()); | |
} catch (Program.CyclicProgramException e) { | |
insn.flip(); | |
continue; | |
} | |
break; | |
} | |
} | |
public interface Program { | |
public static Interpreted from(InputStream stream) { | |
return from(new InputStreamReader(stream)); | |
} | |
public static Interpreted from(Reader reader) { | |
return from(new BufferedReader(reader)); | |
} | |
public static Interpreted from(BufferedReader bufferedReader) { | |
List<Instruction> instructions = bufferedReader | |
.lines() | |
.filter(s -> !s.isEmpty()) | |
.map(Instruction::read) | |
.collect(Collectors.toList()); | |
return from(instructions); | |
} | |
public static Interpreted from(List<Instruction> instructions) { | |
return new Interpreted(instructions); | |
} | |
class Interpreted implements Program { | |
public List<Instruction> instructions; | |
public Interpreted(List<Instruction> instructions) { | |
this.instructions = instructions; | |
} | |
@Override | |
public int run() throws CyclicProgramException { | |
boolean[] visited = new boolean[instructions.size()]; | |
Instruction instruction; | |
int program = 0; | |
int accumulator = 0; | |
while (program < instructions.size()) { | |
if (visited[program]) { | |
throw new CyclicProgramException(accumulator); | |
} | |
visited[program] = true; | |
instruction = instructions.get(program); | |
program = instruction.advanceProgram(program); | |
accumulator = instruction.advanceAccumulator(accumulator); | |
} | |
return accumulator; | |
} | |
private static class Loader extends ClassLoader { | |
public Class<?> defineClass(String name, byte[] bytes) { | |
return defineClass(name, bytes, 0, bytes.length); | |
} | |
} | |
private static int names = 0; | |
@Override | |
public Program compile() { | |
String pClassName = Program.class | |
.getName() | |
.replace('.', '/'); | |
String className = pClassName + "$Compiled" + names++; | |
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); | |
cw.visit(V1_1, | |
ACC_PUBLIC, | |
className, | |
null, | |
"java/lang/Object", | |
new String[] { pClassName }); | |
Method m = Method.getMethod("void <init> ()"); | |
GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); | |
mg.loadThis(); | |
mg.invokeConstructor(Type.getType(Object.class), m); | |
mg.returnValue(); | |
mg.endMethod(); | |
m = Method.getMethod("int run ()"); | |
mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); | |
Label[] labels = new Label[instructions.size() + 1]; | |
for (int i = 0; i <= instructions.size(); ++i) { | |
labels[i] = mg.newLabel(); | |
} | |
int seen = mg.newLocal(Type.getType(boolean[].class)); | |
mg.push(instructions.size()); | |
mg.newArray(Type.BOOLEAN_TYPE); | |
mg.storeLocal(seen); | |
int accumulator = mg.newLocal(Type.getType(int.class)); | |
mg.push(0); | |
mg.storeLocal(accumulator); | |
Label postThrow = mg.newLabel(); | |
mg.goTo(postThrow); | |
Label doThrow = mg.mark(); | |
mg.newInstance(Type.getType(CyclicProgramException.class)); | |
mg.dup(); | |
mg.loadLocal(accumulator); | |
mg.invokeConstructor(Type.getType(CyclicProgramException.class), | |
Method.getMethod("void <init>(int)")); | |
mg.throwException(); | |
mg.mark(postThrow); | |
int i = 0; | |
for (Instruction insn : instructions) { | |
insn.compile(mg, seen, accumulator, labels, i, doThrow); | |
i++; | |
} | |
mg.mark(labels[labels.length - 1]); | |
mg.loadLocal(accumulator); | |
mg.returnValue(); | |
mg.endMethod(); | |
m = new Method("compile", "()L" + pClassName + ";"); | |
mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); | |
mg.loadThis(); | |
mg.returnValue(); | |
mg.endMethod(); | |
m = Method.getMethod("String toString ()"); | |
mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); | |
mg.push("<compiled program>"); | |
mg.returnValue(); | |
mg.endMethod(); | |
cw.visitEnd(); | |
try { | |
return (Program) new Loader() | |
.defineClass(className, cw.toByteArray()) | |
.getConstructor() | |
.newInstance(); | |
} catch (Exception | Error e) { | |
e.printStackTrace(); | |
return this; | |
} | |
} | |
@Override | |
public String toString() { | |
return instructions.toString(); | |
} | |
} | |
int run() throws CyclicProgramException; | |
Program compile(); | |
class CyclicProgramException extends Exception { | |
public final int accumulator; | |
public CyclicProgramException(int accumulator) { | |
super(String.format("Cyclic program. (Accumulator: %s)", | |
accumulator)); | |
this.accumulator = accumulator; | |
} | |
} | |
public class Instruction { | |
public static enum Operation { | |
NOP, | |
JMP, | |
ACC, | |
; | |
public int advanceAccumulator(int accumulator, int value) { | |
return accumulator + (this == ACC ? value : 0); | |
} | |
public int advanceProgram(int program, int value) { | |
return program + (this == JMP ? value : 1); | |
} | |
public void compile(GeneratorAdapter mg, int accumulator, | |
Label[] labels, int index, int value) { | |
switch (this) { | |
case NOP: | |
break; | |
case JMP: | |
mg.goTo(labels[index + value >= labels.length ? | |
labels.length - 1 : | |
index + value]); | |
break; | |
case ACC: | |
mg.loadLocal(accumulator); | |
mg.push(value); | |
mg.math(GeneratorAdapter.ADD, Type.INT_TYPE); | |
mg.storeLocal(accumulator); | |
break; | |
} | |
} | |
public Operation flip() { | |
switch (this) { | |
case NOP: | |
return JMP; | |
case JMP: | |
return NOP; | |
default: | |
return this; | |
} | |
} | |
} | |
public Operation code; | |
public int value; | |
public Instruction(Operation code, int value) { | |
this.code = code; | |
this.value = value; | |
} | |
public static Instruction read(String line) { | |
return new Instruction(Operation.valueOf(line.substring(0, 3).toUpperCase()), | |
Integer.parseInt(line.substring(4))); | |
} | |
public int advanceAccumulator(int accumulator) { | |
return code.advanceAccumulator(accumulator, value); | |
} | |
public int advanceProgram(int program) { | |
return code.advanceProgram(program, value); | |
} | |
public boolean flip() { | |
return code != (code = code.flip()); | |
} | |
public void compile(GeneratorAdapter mg, int seen, int accumulator, | |
Label[] labels, int index, Label doThrow) { | |
mg.mark(labels[index]); | |
mg.loadLocal(seen); | |
mg.push(index); | |
mg.arrayLoad(Type.BOOLEAN_TYPE); | |
mg.ifZCmp(GeneratorAdapter.NE, doThrow); | |
mg.loadLocal(seen); | |
mg.push(index); | |
mg.push(true); | |
mg.arrayStore(Type.BOOLEAN_TYPE); | |
code.compile(mg, accumulator, labels, index, value); | |
} | |
@Override | |
public String toString() { | |
return code.toString() + " " + value; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment