ソースを参照

All operations working

boono 11 ヶ月 前
コミット
b8263e2839

+ 11 - 0
src/main/java/xyz/kwiecien/jfuck/operation/BytecodeConstants.java

@@ -1,6 +1,17 @@
 package xyz.kwiecien.jfuck.operation;
 
+import java.lang.constant.ClassDesc;
+
 public interface BytecodeConstants {
+    String DATA_SIZE_CONSTANT_NAME = "DATA_SIZE";
+    int DATA_SIZE = 30000;
+
     int PTR_VAR_INDEX = 1;
     int DATA_VAR_INDEX = 2;
+    int SCANNER_VAR_INDEX = 3;
+    ClassDesc SYSTEM_DESC = ClassDesc.of("java.lang.System");
+    ClassDesc PRINT_STREAM_DESC = ClassDesc.of("java.io.PrintStream");
+    ClassDesc INPUT_STREAM_DESC = ClassDesc.of("java.io.InputStream");
+    ClassDesc SCANNER_DESC = ClassDesc.of("java.util.Scanner");
+    ClassDesc STRING_CLASS_DESC = ClassDesc.of("java.lang.String");
 }

+ 9 - 3
src/main/java/xyz/kwiecien/jfuck/operation/Initialization.java

@@ -2,16 +2,22 @@ package xyz.kwiecien.jfuck.operation;
 
 import java.lang.classfile.CodeBuilder;
 import java.lang.classfile.TypeKind;
+import java.lang.constant.MethodTypeDesc;
 import java.util.function.Consumer;
 
-import static xyz.kwiecien.jfuck.operation.BytecodeConstants.DATA_VAR_INDEX;
-import static xyz.kwiecien.jfuck.operation.BytecodeConstants.PTR_VAR_INDEX;
+import static java.lang.constant.ConstantDescs.CD_void;
+import static java.lang.constant.ConstantDescs.INIT_NAME;
+import static xyz.kwiecien.jfuck.operation.BytecodeConstants.*;
 
 public class Initialization implements Operation {
     @Override
     public Consumer<CodeBuilder> appendBytecode() {
         return c -> c
                 .iconst_0().istore(PTR_VAR_INDEX) // Pointer init
-                .sipush(30000).newarray(TypeKind.BYTE).astore(DATA_VAR_INDEX); // Initialize array of 30k bytes
+                .sipush(DATA_SIZE).newarray(TypeKind.BYTE).astore(DATA_VAR_INDEX) // Initialize array of 30k bytes
+                .new_(SCANNER_DESC).dup() //dup, because we will be initializing it and then setting a field
+                .getstatic(SYSTEM_DESC, "in", INPUT_STREAM_DESC)
+                .invokespecial(SCANNER_DESC, INIT_NAME, MethodTypeDesc.of(CD_void, INPUT_STREAM_DESC))
+                .astore(SCANNER_VAR_INDEX);
     }
 }

+ 7 - 1
src/main/java/xyz/kwiecien/jfuck/operation/ModifyPointerOperation.java

@@ -3,9 +3,15 @@ package xyz.kwiecien.jfuck.operation;
 import java.lang.classfile.CodeBuilder;
 import java.util.function.Consumer;
 
+import static xyz.kwiecien.jfuck.operation.BytecodeConstants.DATA_SIZE;
+import static xyz.kwiecien.jfuck.operation.BytecodeConstants.PTR_VAR_INDEX;
+
 public record ModifyPointerOperation(int value) implements Operation {
     @Override
     public Consumer<CodeBuilder> appendBytecode() {
-        return c -> {};
+        return c -> c.iload(PTR_VAR_INDEX)
+                .ldc(value).iadd() // ptr = ptr + value
+                .ldc(DATA_SIZE).iadd().ldc(DATA_SIZE).irem() // ptr = (ptr + DATA_SIZE) % DATA_SIZE
+                .istore(PTR_VAR_INDEX);
     }
 }

+ 11 - 1
src/main/java/xyz/kwiecien/jfuck/operation/ReadOperation.java

@@ -1,11 +1,21 @@
 package xyz.kwiecien.jfuck.operation;
 
 import java.lang.classfile.CodeBuilder;
+import java.lang.constant.MethodTypeDesc;
 import java.util.function.Consumer;
 
+import static java.lang.constant.ConstantDescs.*;
+import static xyz.kwiecien.jfuck.operation.BytecodeConstants.*;
+
 public class ReadOperation implements Operation {
     @Override
     public Consumer<CodeBuilder> appendBytecode() {
-        return c -> {};
+        return c -> c.aload(DATA_VAR_INDEX).iload(PTR_VAR_INDEX)
+                .aload(SCANNER_VAR_INDEX)
+                .invokevirtual(SCANNER_DESC, "next", MethodTypeDesc.of(CD_String))
+                .iconst_0()
+                .invokevirtual(CD_String, "charAt", MethodTypeDesc.of(CD_char, CD_int))
+                .i2b()
+                .bastore();
     }
 }

+ 4 - 10
src/main/java/xyz/kwiecien/jfuck/operation/WriteOperation.java

@@ -2,9 +2,6 @@ package xyz.kwiecien.jfuck.operation;
 
 import java.lang.classfile.CodeBuilder;
 import java.lang.classfile.TypeKind;
-import java.lang.classfile.constantpool.MethodRefEntry;
-import java.lang.constant.ClassDesc;
-import java.lang.constant.ConstantDescs;
 import java.lang.constant.MethodTypeDesc;
 import java.util.function.Consumer;
 
@@ -13,18 +10,15 @@ import static xyz.kwiecien.jfuck.operation.BytecodeConstants.DATA_VAR_INDEX;
 import static xyz.kwiecien.jfuck.operation.BytecodeConstants.PTR_VAR_INDEX;
 
 public class WriteOperation implements Operation {
-    private static final ClassDesc SYSTEM_DESC = ClassDesc.of("java.lang.System");
-    private static final ClassDesc PRINT_STREAM_DESC = ClassDesc.of("java.io.PrintStream");
-    private static final ClassDesc STRING_CLASS_DESC = ClassDesc.of("java.lang.String");
     @Override
     public Consumer<CodeBuilder> appendBytecode() {
-        return c -> c.getstatic(SYSTEM_DESC, "out", PRINT_STREAM_DESC)
-                .new_(STRING_CLASS_DESC).dup() // dup, because we will be initializing it and then using as an argument for print
+        return c -> c.getstatic(BytecodeConstants.SYSTEM_DESC, "out", BytecodeConstants.PRINT_STREAM_DESC)
+                .new_(BytecodeConstants.STRING_CLASS_DESC).dup() // dup, because we will be initializing it and then using as an argument for print
                 .iconst_1().newarray(TypeKind.BYTE).dup() // dup, because we will be filling it with data and then passing to String.<init>
                 .iconst_0() // index of byte array to set
                 .aload(DATA_VAR_INDEX).iload(PTR_VAR_INDEX).baload() //Load data at pointer
                 .bastore() // store loaded data at index zero
-                .invokespecial(STRING_CLASS_DESC, INIT_NAME, MethodTypeDesc.of(CD_void, CD_byte.arrayType()))
-                .invokevirtual(PRINT_STREAM_DESC, "print", MethodTypeDesc.of(CD_void, CD_String));
+                .invokespecial(BytecodeConstants.STRING_CLASS_DESC, INIT_NAME, MethodTypeDesc.of(CD_void, CD_byte.arrayType()))
+                .invokevirtual(BytecodeConstants.PRINT_STREAM_DESC, "print", MethodTypeDesc.of(CD_void, CD_String));
     }
 }

+ 40 - 21
src/test/java/OperationsTest.java

@@ -1,7 +1,5 @@
 import org.junit.jupiter.api.Test;
-import xyz.kwiecien.jfuck.operation.Initialization;
-import xyz.kwiecien.jfuck.operation.ModifyStackValueOperation;
-import xyz.kwiecien.jfuck.operation.WriteOperation;
+import xyz.kwiecien.jfuck.operation.*;
 
 import java.io.IOException;
 import java.lang.classfile.ClassFile;
@@ -10,9 +8,12 @@ import java.lang.constant.ClassDesc;
 import java.lang.constant.MethodTypeDesc;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.List;
 import java.util.function.Consumer;
 
+import static java.lang.classfile.ClassFile.*;
 import static java.lang.constant.ConstantDescs.*;
+import static xyz.kwiecien.jfuck.operation.BytecodeConstants.DATA_SIZE_CONSTANT_NAME;
 
 public class OperationsTest {
     @Test
@@ -21,25 +22,43 @@ public class OperationsTest {
                 .aload(0)
                 .invokespecial(CD_Object, INIT_NAME, MTD_void).return_();
 
-                Consumer<CodeBuilder> mainCode = cb -> {
-                    new Initialization().appendBytecode().accept(cb);
-                    new ModifyStackValueOperation((byte) 0x30).appendBytecode().accept(cb);
-                    new WriteOperation().appendBytecode().accept(cb);
-                    cb.return_();
-                };
+        Consumer<CodeBuilder> addCode = cb -> {
+            new Initialization().appendBytecode().accept(cb);
+            new ReadOperation().appendBytecode().accept(cb);
+            new ModifyPointerOperation(1).appendBytecode().accept(cb);
+            new ModifyStackValueOperation((byte) 6).appendBytecode().accept(cb);
+            new Loop(List.of(
+                    new ModifyPointerOperation(-1),
+                    new ModifyStackValueOperation((byte) -8),
+                    new ModifyPointerOperation(1),
+                    new ModifyStackValueOperation((byte) -1)
+            )).appendBytecode().accept(cb);
+            new ReadOperation().appendBytecode().accept(cb);
+            new Loop(List.of(
+                    new ModifyPointerOperation(-1),
+                    new ModifyStackValueOperation((byte) 1),
+                    new ModifyPointerOperation(1),
+                    new ModifyStackValueOperation((byte) -1)
+            )).appendBytecode().accept(cb);
+            new ModifyPointerOperation(-1).appendBytecode().accept(cb);
+            new WriteOperation().appendBytecode().accept(cb);
+            cb.return_();
+        };
 
-        var bytes = ClassFile.of().build(ClassDesc.of("GenClass"), clb ->
-                clb.withFlags(ClassFile.ACC_PUBLIC)
-                        .withMethod(
-                                INIT_NAME,
-                                MTD_void,
-                                ClassFile.ACC_PUBLIC,
-                                mb -> mb.withCode(initCode))
-                        .withMethod(
-                                "main",
-                                MethodTypeDesc.of(CD_void, CD_String.arrayType()),
-                                ClassFile.ACC_PUBLIC + ClassFile.ACC_STATIC,
-                                mb -> mb.withCode(mainCode)));
+        var bytes = ClassFile.of().build(ClassDesc.of("GenClass"), clb -> {
+            clb.withFlags(ACC_PUBLIC);
+            clb.withField(DATA_SIZE_CONSTANT_NAME, CD_int, ACC_STATIC | ACC_FINAL);
+            clb.withMethod(
+                            INIT_NAME,
+                            MTD_void,
+                            ACC_PUBLIC,
+                            mb -> mb.withCode(initCode))
+                    .withMethod(
+                            "main",
+                            MethodTypeDesc.of(CD_void, CD_String.arrayType()),
+                            ACC_PUBLIC + ACC_STATIC,
+                            mb -> mb.withCode(addCode));
+        });
         Files.write(Path.of("/tmp/GenClass.class"), bytes);
     }
 }