feat: the start of soft2butbad

This commit is contained in:
Ashhhleyyy 2024-03-12 16:59:21 +00:00
commit 205e12ee1b
Signed by: ash
GPG key ID: 83B789081A0878FB
3 changed files with 246 additions and 0 deletions

View file

@ -0,0 +1,7 @@
package tools;
public class InvalidOperationException extends Exception {
public InvalidOperationException(String message) {
super(message);
}
}

17
soft2butbad/Main.java Normal file
View file

@ -0,0 +1,17 @@
package tools;
public class Main {
public static void main(String[] args) throws InvalidOperationException {
TallyCounter counter = new TallyCounter();
System.out.println(counter);
for (int i = 0; i < 999; i++) {
counter.increment();
}
counter.decrement();
System.out.println(counter);
counter.reset();
counter.increment();
System.out.println(counter);
counter.reset();
}
}

View file

@ -0,0 +1,222 @@
package tools;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.LabelNode;
import dev.ashhhleyyy.bad.framework.ClassMaker;
public class TallyCounter {
private static final ClassMaker maker = new ClassMaker("tools/TallyCounterImpl_");
private final ITallyCounter impl;
public TallyCounter() {
this(3);
}
public TallyCounter(int digits) {
try {
Class<?> cls = maker.makeClass(builder -> {
builder.iface("tools/TallyCounter$ITallyCounter");
builder.field(
Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "counter", "I", null, null
);
builder.field(
Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "digits", "I", null, null
);
builder.field(
Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, "maxValue", "I", null, null
);
builder.method(
Opcodes.ACC_PUBLIC,
"<init>",
"(I)V",
null,
new String[0],
bytecode -> {
// super();
bytecode.aload(0);
bytecode.dup();
bytecode.dup();
bytecode.dup();
bytecode.invokespecial("java/lang/Object", "<init>", "()V");
// this.counter = 0;
bytecode.bipush(0);
bytecode.putThisField("counter", "I");
// this.digits = digits;
bytecode.iload(1);
bytecode.putThisField("digits", "I");
// this.maxValue = ((int) Math.pow(10, digits)) - 1;
bytecode.ldc(10.0d);
bytecode.iload(1);
bytecode.i2d();
bytecode.invokestatic("java/lang/Math", "pow", "(DD)D");
bytecode.d2i();
bytecode.bipush(1);
bytecode.isub();
bytecode.putThisField("maxValue", "I");
bytecode.$return();
});
builder.method(
Opcodes.ACC_PUBLIC,
"read",
"()I",
null,
new String[0],
bytecode -> {
bytecode.aload(0);
bytecode.getThisField("counter", "I");
bytecode.ireturn();
});
builder.method(
Opcodes.ACC_PUBLIC,
"reset",
"()V",
null,
new String[0],
bytecode -> {
bytecode.aload(0);
bytecode.bipush(0);
bytecode.putThisField("counter", "I");
bytecode.$return();
});
builder.method(
Opcodes.ACC_PUBLIC,
"toString",
"()Ljava/lang/String;",
null,
new String[0],
bytecode -> {
// return String.format("%03d", this.counter);
bytecode.$new("java/lang/StringBuilder");
bytecode.ldc("%03d");
bytecode.bipush(1);
bytecode.newarray("java/lang/Object");
bytecode.dup();
bytecode.bipush(0);
bytecode.aload(0);
bytecode.getThisField("counter", "I");
bytecode.invokestatic("java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
bytecode.aastore();
bytecode.invokestatic("java/lang/String", "format", "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;");
bytecode.areturn();
});
builder.method(
Opcodes.ACC_PUBLIC,
"increment",
"()V",
null,
new String[] { "tools/InvalidOperationException" },
bytecode -> {
// if (this.counter < this.maxValue) {
bytecode.aload(0);
bytecode.getThisField("counter", "I");
bytecode.aload(0);
bytecode.getThisField("maxValue", "I");
LabelNode label = bytecode.if_icmpge();
// this.counter = this.counter + 1;
bytecode.aload(0);
bytecode.dup();
bytecode.getThisField("counter", "I");
bytecode.bipush(1);
bytecode.iadd();
bytecode.putThisField("counter", "I");
bytecode.$return();
// } else {
bytecode.add(label);
// throw new InvalidOperationException("this counter has reached it's counting limit");
bytecode.$new("tools/InvalidOperationException");
bytecode.dup();
bytecode.ldc("this counter has reached it's counting limit");
bytecode.invokespecial("tools/InvalidOperationException", "<init>", "(Ljava/lang/String;)V");
bytecode.athrow();
// }
});
builder.method(
Opcodes.ACC_PUBLIC,
"decrement",
"()V",
null,
new String[] { "tools/InvalidOperationException" },
bytecode -> {
// if (this.counter > 0) {
bytecode.aload(0);
bytecode.getThisField("counter", "I");
bytecode.bipush(0);
LabelNode label = bytecode.if_icmple();
// this.counter = this.counter - 1;
bytecode.aload(0);
bytecode.dup();
bytecode.getThisField("counter", "I");
bytecode.bipush(1);
bytecode.isub();
bytecode.putThisField("counter", "I");
bytecode.$return();
// } else {
bytecode.add(label);
// throw new InvalidOperationException("counters can't count that low");
bytecode.$new("tools/InvalidOperationException");
bytecode.dup();
bytecode.ldc("counters can't count that low");
bytecode.invokespecial("tools/InvalidOperationException", "<init>", "(Ljava/lang/String;)V");
bytecode.athrow();
// }
});
});
this.impl = (ITallyCounter) cls.getConstructor(int.class).newInstance(digits);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public int read() {
return this.impl.read();
}
public void reset() {
this.impl.reset();
}
public void increment() throws InvalidOperationException {
this.impl.increment();
}
public void decrement() throws InvalidOperationException {
this.impl.decrement();
}
@Override
public String toString() {
return this.impl.toString();
}
public interface ITallyCounter {
void increment() throws InvalidOperationException;
void decrement() throws InvalidOperationException;
int read();
void reset();
String toString();
}
}