feat: the start of soft2butbad
This commit is contained in:
commit
205e12ee1b
3 changed files with 246 additions and 0 deletions
7
soft2butbad/InvalidOperationException.java
Normal file
7
soft2butbad/InvalidOperationException.java
Normal file
|
@ -0,0 +1,7 @@
|
|||
package tools;
|
||||
|
||||
public class InvalidOperationException extends Exception {
|
||||
public InvalidOperationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
17
soft2butbad/Main.java
Normal file
17
soft2butbad/Main.java
Normal 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();
|
||||
}
|
||||
}
|
222
soft2butbad/TallyCounter.java
Normal file
222
soft2butbad/TallyCounter.java
Normal 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();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue