diff --git a/Assignment_1.pdf b/Assignment_1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a077aa47dfdcf1f56b670b6e8da419f8ac541ffe Binary files /dev/null and b/Assignment_1.pdf differ diff --git a/build.xml b/build.xml new file mode 100644 index 0000000000000000000000000000000000000000..658996f9b1fa3d00605af37f0e167f0d8411ef3d --- /dev/null +++ b/build.xml @@ -0,0 +1,43 @@ +<project name="Project" default="classes"> + <target name="classes"> + <mkdir dir="classes" /> + <javac srcdir="src:test" destdir="classes" + classpath="lib/junit-4.11.jar;lib/hamcrest-core-1.3.jar" + includeantruntime="false"/> + </target> + <target name="clean"> + <delete dir="classes" /> + </target> + + <target name="run" depends="classes"> + <java classname="swen90006.machine.SimpleDriver" + classpath="classes/;lib/junit-4.11.jar;lib/hamcrest-core-1.3.jar"> + </java> + </target> + + <target name="partitioning" depends="classes"> + <junit printsummary="yes" fork="yes" haltonfailure="yes"> + <classpath> + <pathelement path="classes/"/> + <pathelement path="lib/junit-4.11.jar"/> + <pathelement path="lib/hamcrest-core-1.3.jar"/> + </classpath> + <formatter type="plain"/> + <test name="swen90006.machine.PartitioningTests"/> + </junit> + </target> + + <target name="boundary" depends="classes"> + <junit printsummary="yes" fork="yes" haltonfailure="yes"> + <classpath> + <pathelement path="classes/"/> + <pathelement path="lib/junit-4.11.jar"/> + <pathelement path="lib/hamcrest-core-1.3.jar"/> + </classpath> + <formatter type="plain"/> + <test name="swen90006.machine.BoundaryTests"/> + </junit> + </target> + + +</project> diff --git a/classes/swen90006/machine/BoundaryTests.class b/classes/swen90006/machine/BoundaryTests.class new file mode 100644 index 0000000000000000000000000000000000000000..75ca15dbf4f906cd2b7beec235fa91a8de079579 Binary files /dev/null and b/classes/swen90006/machine/BoundaryTests.class differ diff --git a/classes/swen90006/machine/BugException.class b/classes/swen90006/machine/BugException.class new file mode 100644 index 0000000000000000000000000000000000000000..f118d84a4f1eb3cda19ec8de216e3dc339d6090c Binary files /dev/null and b/classes/swen90006/machine/BugException.class differ diff --git a/classes/swen90006/machine/InvalidInstructionException.class b/classes/swen90006/machine/InvalidInstructionException.class new file mode 100644 index 0000000000000000000000000000000000000000..b825f28f0bae9bc7d1352fc96ae041000ea4a729 Binary files /dev/null and b/classes/swen90006/machine/InvalidInstructionException.class differ diff --git a/classes/swen90006/machine/Machine.class b/classes/swen90006/machine/Machine.class new file mode 100644 index 0000000000000000000000000000000000000000..bc67cdff3d66160515599de443f4aea563326bac Binary files /dev/null and b/classes/swen90006/machine/Machine.class differ diff --git a/classes/swen90006/machine/NoReturnValueException.class b/classes/swen90006/machine/NoReturnValueException.class new file mode 100644 index 0000000000000000000000000000000000000000..8e5254e009b57eb2894b9bb735e33962cd163259 Binary files /dev/null and b/classes/swen90006/machine/NoReturnValueException.class differ diff --git a/classes/swen90006/machine/PartitioningTests.class b/classes/swen90006/machine/PartitioningTests.class new file mode 100644 index 0000000000000000000000000000000000000000..af52566636122d3ab4edeca3dfd4fb3e867aa1db Binary files /dev/null and b/classes/swen90006/machine/PartitioningTests.class differ diff --git a/classes/swen90006/machine/SimpleDriver.class b/classes/swen90006/machine/SimpleDriver.class new file mode 100644 index 0000000000000000000000000000000000000000..e2898a8d0db987dacaa7428260b0686b6d57b7e3 Binary files /dev/null and b/classes/swen90006/machine/SimpleDriver.class differ diff --git a/examples/array.s b/examples/array.s new file mode 100644 index 0000000000000000000000000000000000000000..54b5878259fee8b451aebed898ebd70cda25caa0 --- /dev/null +++ b/examples/array.s @@ -0,0 +1,40 @@ + ;; array program. Fills an array with the values 0 ... N-1 + ;; and then interates through the array, summing its elements + + ;; global constants: + ;; R3 holds 'N', the length of the array + ;; R2 holds 1, the increment value used below + MOV R3 10 ; N = 10 + MOV R2 1 ; + + ;; create the array + ;; local variables + ;; R1 holds 'i', which is a counter from 0 .. N-1 + ;; R0 holds 'p', the address of the array's ith element + MOV R1 0 ; i = 0; + MOV R0 100 + + SUB R4 R3 R1 ; while(i != N) + JZ R4 5 ; { + STR R0 0 R1 ; *p = i; + ADD R1 R1 R2 ; i = i + 1; + ADD R0 R0 R2 ; p = p + 1; + JMP -5 ; } + + ;; sum up the array + ;; local variables + ;; R1 holds 'i', which is a counter from 0 .. N-1 + ;; R0 holds 'p', the address of the array's ith element + ;; R5 holds 'sum', which always holds the sum of the array's first i elements + MOV R1 0 ; i = 0; + MOV R0 100 + MOV R5 0 ; sum = 0; + + SUB R4 R3 R1 ; while(i != N) + JZ R4 6 ; { + LDR R4 R0 0 ; + ADD R5 R5 R4 ; sum = sum + *p; + ADD R0 R0 R2 ; p = p + 1; + ADD R1 R1 R2 ; i = i + 1; + JMP -6 ; } + RET R5 ; return sum; diff --git a/examples/factorial.s b/examples/factorial.s new file mode 100644 index 0000000000000000000000000000000000000000..2928e12e5d977b84e4ccfbb637a1771c2bc44ed3 --- /dev/null +++ b/examples/factorial.s @@ -0,0 +1,22 @@ + ;; factorial program, to calculate N! + + ;; global constants: + ;; R3 holds 'N', the thing we are computing factorial of + ;; R2 holds 1, the increment value used below + MOV R3 12 ; N = 12 + MOV R2 1 ; + + ;; local variables + ;; R1 holds 'i', which is a counter from 0 .. N + ;; R0 holds 'n', which is always equal to i! + MOV R1 0 ; i = 0; + MOV R0 1 ; n = 1; + + ;; program body + ;; loop invariant (see SWEN90010 next semester): n = i! + SUB R4 R3 R1 ; while(i != N) + JZ R4 4 ; { + ADD R1 R1 R2 ; i = i + 1; + MUL R0 R0 R1 ; n = n * i; + JMP -4 ; } + RET R0 ; return n; diff --git a/lib/hamcrest-core-1.3.jar b/lib/hamcrest-core-1.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..9d5fe16e3dd37ebe79a36f61f5d0e1a69a653a8a Binary files /dev/null and b/lib/hamcrest-core-1.3.jar differ diff --git a/lib/junit-4.11.jar b/lib/junit-4.11.jar new file mode 100644 index 0000000000000000000000000000000000000000..aaf74448492932e95902b40a70c7a4da5bad4744 Binary files /dev/null and b/lib/junit-4.11.jar differ diff --git a/machine.zip b/machine.zip new file mode 100644 index 0000000000000000000000000000000000000000..1d51cbc55e5d55a7f69fea8a60be3eac510b9bd9 Binary files /dev/null and b/machine.zip differ diff --git a/mutants/mutant-1/swen90006/machine/BugException.java b/mutants/mutant-1/swen90006/machine/BugException.java new file mode 100644 index 0000000000000000000000000000000000000000..41cb9cab9f4bb06efde089f190abc13331b00b21 --- /dev/null +++ b/mutants/mutant-1/swen90006/machine/BugException.java @@ -0,0 +1,7 @@ +package swen90006.machine; + +public class BugException extends Exception { + public BugException(String msg){ + super("You triggered one of the security bugs!\n" + msg); + } +} diff --git a/mutants/mutant-1/swen90006/machine/InvalidInstructionException.java b/mutants/mutant-1/swen90006/machine/InvalidInstructionException.java new file mode 100644 index 0000000000000000000000000000000000000000..25e042bfb4bf0519927e165946fa9d03640c45fe --- /dev/null +++ b/mutants/mutant-1/swen90006/machine/InvalidInstructionException.java @@ -0,0 +1,9 @@ +package swen90006.machine; + +public class InvalidInstructionException extends RuntimeException +{ + public String toString() + { + return "InvalidInstructionException"; + } +} diff --git a/mutants/mutant-1/swen90006/machine/Machine.java b/mutants/mutant-1/swen90006/machine/Machine.java new file mode 100644 index 0000000000000000000000000000000000000000..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 --- /dev/null +++ b/mutants/mutant-1/swen90006/machine/Machine.java @@ -0,0 +1,303 @@ +package swen90006.machine; + +import java.util.Arrays; +import java.util.List; + +public class Machine +{ + /** arithmetic instructions each take three registers as arguments with the + * destination register appearing first + */ + + /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ + public static final String INSTRUCTION_ADD = "add"; + + /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ + public static final String INSTRUCTION_SUBTRACT = "sub"; + + /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ + public static final String INSTRUCTION_MULT = "mul"; + + /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ + public static final String INSTRUCTION_DIVIDE = "div"; + + /** ret rs =~ return rs */ + public static final String INSTRUCTION_RETURN = "ret"; + + /** ldr rd rs offs =~ rd = rs[offs] */ + public static final String INSTRUCTION_LOAD = "ldr"; + + /** str ra offs rb =~ ra[offs] = rb */ + public static final String INSTRUCTION_STORE = "str"; + + /** mov rd val =~ rd = val */ + public static final String INSTRUCTION_MOVE = "mov"; + + /** jmp offs =~ pc = pc + offs */ + public static final String INSTRUCTION_JUMP = "jmp"; + + /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ + public static final String INSTRUCTION_JZ = "jz"; + + public static final int NUM_REGS = 32; + public static final int MAX_REG = (NUM_REGS - 1); + public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ + public static final int MAX_ADDR = MEMORY_SIZE-1; + + private int[] memory; + private int[] regs; + + private int count = 0; /* counts number of instructions executed so far */ + + public Machine() + { + memory = new int[MEMORY_SIZE]; + regs = new int[NUM_REGS]; + count = 0; + } + + private void do_add(int dest, int src1, int src2) + { + regs[dest] = regs[src1] + regs[src2]; + } + + private void do_sub(int dest, int src1, int src2) + { + regs[dest] = regs[src1] - regs[src2]; + } + + private void do_mult(int dest, int src1, int src2) + { + regs[dest] = regs[src1] * regs[src2]; + } + + private void do_div(int dest, int src1, int src2) + { + if (regs[src2] == 0){ + /* no op */ + }else{ + regs[dest] = regs[src1] / regs[src2]; + } + } + + private void do_load(int dest, int src, int offs) { + if (regs[src] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[src] + offs < 0){ + /* no op */ + }else{ + regs[dest] = memory[regs[src] + offs]; + } + } + + private void do_store(int a, int offs, int b) { + if (regs[a] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[a] + offs < 0){ + /* no op */ + }else{ + memory[regs[a] + offs] = regs[b]; + } + } + + private void do_move(int rd, int val){ + regs[rd] = val; + } + + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); + } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); + } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_reg(num); + return num; + } + + private int parseOffset(String s) + throws InvalidInstructionException + { + int num = 0; + try { + num = Integer.parseInt(s); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_offset(num); + return num; + } + + + private void validate_reg(int reg) + throws InvalidInstructionException + { + if (reg < 0 || reg > MAX_REG) { + throw new InvalidInstructionException(); + } + } + + private void validate_offset(int offset) + throws InvalidInstructionException + { + if (offset < -MAX_ADDR || offset > MAX_ADDR) { + throw new InvalidInstructionException(); + } + } + + /** Execute an assembly program. + * + * @param prog is the program to execute as an iterable collection of strings, + * each of which is a single instruction. + * @return the program's return value. + * @throws Exception when program has unrecognised or + * invalid instructions, or when it returns no result when it finishes + */ + int execute(List<String> instructions) + throws InvalidInstructionException, + NoReturnValueException + { + + int instructionsExecuted = 0; + int pc = 0; + final int progLength = instructions.size(); + while(true){ + if (pc < 0 || pc >= progLength){ + /* will cause NoReturnValueException to be thrown + * but that is not a bug and and indeed is what the + * VM is supposed to do if the pc becomes negative, + * since in this case the program's execution + * finishes early without a return value having + * been produced. */ + break; + } + String inst = instructions.get(pc); + /* strip leading and trailing whitespace */ + inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); + /* strip out any comments */ + String[] toks = inst.split(";"); + inst = toks[0]; + + /* check for blank lines */ + if (inst.equals("")){ + pc = pc + 1; + count++; + continue; + } + + instructionsExecuted++; + /* now tokenize by splitting on whitespace */ + toks = inst.split("\\s+"); + + /* check minimum number of tokens */ + if (toks.length < 2){ + throw new InvalidInstructionException(); + } + + if (toks[0].equals(INSTRUCTION_ADD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_add(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_sub(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_MULT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_mult(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_div(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_RETURN)){ + int rs = parseReg(toks[1]); + count++; + return regs[rs]; + } else if (toks[0].equals(INSTRUCTION_LOAD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs = parseReg(toks[2]); + int offs = parseOffset(toks[3]); + do_load(rd,rs,offs); + } else if (toks[0].equals(INSTRUCTION_STORE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + int rb = parseReg(toks[3]); + do_store(ra,offs,rb); + } else if (toks[0].equals(INSTRUCTION_MOVE)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + do_move(rd,offs); + } else if (toks[0].equals(INSTRUCTION_JUMP)){ + if (toks.length != 2){ + throw new InvalidInstructionException(); + } + int offs = parseOffset(toks[1]); + pc = pc + offs; + count++; + continue; /* avoid default increment of pc below */ + } else if (toks[0].equals(INSTRUCTION_JZ)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + if (regs[ra] == 0){ + pc = pc + offs; + }else{ + pc = pc + 1; + } + count++; + continue; /* avoid default increment the pc below */ + } else { + System.err.println("Unrecognised instruction: " + inst); + throw new InvalidInstructionException(); + } + count++; + pc = pc + 1; + } + + /* got here without returning already... */ + throw new NoReturnValueException(); + } + + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } +} diff --git a/mutants/mutant-1/swen90006/machine/NoReturnValueException.java b/mutants/mutant-1/swen90006/machine/NoReturnValueException.java new file mode 100644 index 0000000000000000000000000000000000000000..154907e85d6d8fa059da74d8fbfff17f29de28d3 --- /dev/null +++ b/mutants/mutant-1/swen90006/machine/NoReturnValueException.java @@ -0,0 +1,11 @@ +package swen90006.machine; + +public class NoReturnValueException extends RuntimeException +{ + public NoReturnValueException() {} + + public String toString() + { + return "NoReturnValueException"; + } +} diff --git a/mutants/mutant-1/swen90006/machine/SimpleDriver.java b/mutants/mutant-1/swen90006/machine/SimpleDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..bf288a4d2d3262d62316a7fa8adeb2a9f9a1dd6e --- /dev/null +++ b/mutants/mutant-1/swen90006/machine/SimpleDriver.java @@ -0,0 +1,51 @@ +package swen90006.machine; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.util.List; + +public class SimpleDriver { + + private static final int MEMORY_SIZE = Machine.MAX_ADDR+1; + + public static void main(String[] args) throws Exception + { + if (args.length != 1){ + System.err.println("Usage: java SimpleDriver <inputfile>"); + System.err.println("\nMust have 1 argument instead of " + args.length); + System.exit(1); + } + + Charset charset = Charset.forName("UTF-8"); + + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(args[0]), charset); + }catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + + Machine m = null; + try { + System.out.println("There are this many instructions: " + lines.size()); + + m = new Machine(); + + Integer res = null; + res = m.execute(lines); + System.out.println("Program result: " + res + "\n"); + + } catch (Exception e) { + System.err.println("Exception while executing program. (stacktrace follows)"); + e.printStackTrace(System.err); + if (m != null){ + System.err.println("Number of instructions executed before exception: " + m.getCount()); + } + System.exit(1); + } + } +} diff --git a/mutants/mutant-2/swen90006/machine/BugException.java b/mutants/mutant-2/swen90006/machine/BugException.java new file mode 100644 index 0000000000000000000000000000000000000000..41cb9cab9f4bb06efde089f190abc13331b00b21 --- /dev/null +++ b/mutants/mutant-2/swen90006/machine/BugException.java @@ -0,0 +1,7 @@ +package swen90006.machine; + +public class BugException extends Exception { + public BugException(String msg){ + super("You triggered one of the security bugs!\n" + msg); + } +} diff --git a/mutants/mutant-2/swen90006/machine/InvalidInstructionException.java b/mutants/mutant-2/swen90006/machine/InvalidInstructionException.java new file mode 100644 index 0000000000000000000000000000000000000000..25e042bfb4bf0519927e165946fa9d03640c45fe --- /dev/null +++ b/mutants/mutant-2/swen90006/machine/InvalidInstructionException.java @@ -0,0 +1,9 @@ +package swen90006.machine; + +public class InvalidInstructionException extends RuntimeException +{ + public String toString() + { + return "InvalidInstructionException"; + } +} diff --git a/mutants/mutant-2/swen90006/machine/Machine.java b/mutants/mutant-2/swen90006/machine/Machine.java new file mode 100644 index 0000000000000000000000000000000000000000..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 --- /dev/null +++ b/mutants/mutant-2/swen90006/machine/Machine.java @@ -0,0 +1,303 @@ +package swen90006.machine; + +import java.util.Arrays; +import java.util.List; + +public class Machine +{ + /** arithmetic instructions each take three registers as arguments with the + * destination register appearing first + */ + + /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ + public static final String INSTRUCTION_ADD = "add"; + + /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ + public static final String INSTRUCTION_SUBTRACT = "sub"; + + /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ + public static final String INSTRUCTION_MULT = "mul"; + + /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ + public static final String INSTRUCTION_DIVIDE = "div"; + + /** ret rs =~ return rs */ + public static final String INSTRUCTION_RETURN = "ret"; + + /** ldr rd rs offs =~ rd = rs[offs] */ + public static final String INSTRUCTION_LOAD = "ldr"; + + /** str ra offs rb =~ ra[offs] = rb */ + public static final String INSTRUCTION_STORE = "str"; + + /** mov rd val =~ rd = val */ + public static final String INSTRUCTION_MOVE = "mov"; + + /** jmp offs =~ pc = pc + offs */ + public static final String INSTRUCTION_JUMP = "jmp"; + + /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ + public static final String INSTRUCTION_JZ = "jz"; + + public static final int NUM_REGS = 32; + public static final int MAX_REG = (NUM_REGS - 1); + public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ + public static final int MAX_ADDR = MEMORY_SIZE-1; + + private int[] memory; + private int[] regs; + + private int count = 0; /* counts number of instructions executed so far */ + + public Machine() + { + memory = new int[MEMORY_SIZE]; + regs = new int[NUM_REGS]; + count = 0; + } + + private void do_add(int dest, int src1, int src2) + { + regs[dest] = regs[src1] + regs[src2]; + } + + private void do_sub(int dest, int src1, int src2) + { + regs[dest] = regs[src1] - regs[src2]; + } + + private void do_mult(int dest, int src1, int src2) + { + regs[dest] = regs[src1] * regs[src2]; + } + + private void do_div(int dest, int src1, int src2) + { + if (regs[src2] == 0){ + /* no op */ + }else{ + regs[dest] = regs[src1] / regs[src2]; + } + } + + private void do_load(int dest, int src, int offs) { + if (regs[src] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[src] + offs < 0){ + /* no op */ + }else{ + regs[dest] = memory[regs[src] + offs]; + } + } + + private void do_store(int a, int offs, int b) { + if (regs[a] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[a] + offs < 0){ + /* no op */ + }else{ + memory[regs[a] + offs] = regs[b]; + } + } + + private void do_move(int rd, int val){ + regs[rd] = val; + } + + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); + } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); + } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_reg(num); + return num; + } + + private int parseOffset(String s) + throws InvalidInstructionException + { + int num = 0; + try { + num = Integer.parseInt(s); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_offset(num); + return num; + } + + + private void validate_reg(int reg) + throws InvalidInstructionException + { + if (reg < 0 || reg > MAX_REG) { + throw new InvalidInstructionException(); + } + } + + private void validate_offset(int offset) + throws InvalidInstructionException + { + if (offset < -MAX_ADDR || offset > MAX_ADDR) { + throw new InvalidInstructionException(); + } + } + + /** Execute an assembly program. + * + * @param prog is the program to execute as an iterable collection of strings, + * each of which is a single instruction. + * @return the program's return value. + * @throws Exception when program has unrecognised or + * invalid instructions, or when it returns no result when it finishes + */ + int execute(List<String> instructions) + throws InvalidInstructionException, + NoReturnValueException + { + + int instructionsExecuted = 0; + int pc = 0; + final int progLength = instructions.size(); + while(true){ + if (pc < 0 || pc >= progLength){ + /* will cause NoReturnValueException to be thrown + * but that is not a bug and and indeed is what the + * VM is supposed to do if the pc becomes negative, + * since in this case the program's execution + * finishes early without a return value having + * been produced. */ + break; + } + String inst = instructions.get(pc); + /* strip leading and trailing whitespace */ + inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); + /* strip out any comments */ + String[] toks = inst.split(";"); + inst = toks[0]; + + /* check for blank lines */ + if (inst.equals("")){ + pc = pc + 1; + count++; + continue; + } + + instructionsExecuted++; + /* now tokenize by splitting on whitespace */ + toks = inst.split("\\s+"); + + /* check minimum number of tokens */ + if (toks.length < 2){ + throw new InvalidInstructionException(); + } + + if (toks[0].equals(INSTRUCTION_ADD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_add(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_sub(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_MULT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_mult(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_div(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_RETURN)){ + int rs = parseReg(toks[1]); + count++; + return regs[rs]; + } else if (toks[0].equals(INSTRUCTION_LOAD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs = parseReg(toks[2]); + int offs = parseOffset(toks[3]); + do_load(rd,rs,offs); + } else if (toks[0].equals(INSTRUCTION_STORE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + int rb = parseReg(toks[3]); + do_store(ra,offs,rb); + } else if (toks[0].equals(INSTRUCTION_MOVE)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + do_move(rd,offs); + } else if (toks[0].equals(INSTRUCTION_JUMP)){ + if (toks.length != 2){ + throw new InvalidInstructionException(); + } + int offs = parseOffset(toks[1]); + pc = pc + offs; + count++; + continue; /* avoid default increment of pc below */ + } else if (toks[0].equals(INSTRUCTION_JZ)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + if (regs[ra] == 0){ + pc = pc + offs; + }else{ + pc = pc + 1; + } + count++; + continue; /* avoid default increment the pc below */ + } else { + System.err.println("Unrecognised instruction: " + inst); + throw new InvalidInstructionException(); + } + count++; + pc = pc + 1; + } + + /* got here without returning already... */ + throw new NoReturnValueException(); + } + + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } +} diff --git a/mutants/mutant-2/swen90006/machine/NoReturnValueException.java b/mutants/mutant-2/swen90006/machine/NoReturnValueException.java new file mode 100644 index 0000000000000000000000000000000000000000..154907e85d6d8fa059da74d8fbfff17f29de28d3 --- /dev/null +++ b/mutants/mutant-2/swen90006/machine/NoReturnValueException.java @@ -0,0 +1,11 @@ +package swen90006.machine; + +public class NoReturnValueException extends RuntimeException +{ + public NoReturnValueException() {} + + public String toString() + { + return "NoReturnValueException"; + } +} diff --git a/mutants/mutant-2/swen90006/machine/SimpleDriver.java b/mutants/mutant-2/swen90006/machine/SimpleDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..bf288a4d2d3262d62316a7fa8adeb2a9f9a1dd6e --- /dev/null +++ b/mutants/mutant-2/swen90006/machine/SimpleDriver.java @@ -0,0 +1,51 @@ +package swen90006.machine; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.util.List; + +public class SimpleDriver { + + private static final int MEMORY_SIZE = Machine.MAX_ADDR+1; + + public static void main(String[] args) throws Exception + { + if (args.length != 1){ + System.err.println("Usage: java SimpleDriver <inputfile>"); + System.err.println("\nMust have 1 argument instead of " + args.length); + System.exit(1); + } + + Charset charset = Charset.forName("UTF-8"); + + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(args[0]), charset); + }catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + + Machine m = null; + try { + System.out.println("There are this many instructions: " + lines.size()); + + m = new Machine(); + + Integer res = null; + res = m.execute(lines); + System.out.println("Program result: " + res + "\n"); + + } catch (Exception e) { + System.err.println("Exception while executing program. (stacktrace follows)"); + e.printStackTrace(System.err); + if (m != null){ + System.err.println("Number of instructions executed before exception: " + m.getCount()); + } + System.exit(1); + } + } +} diff --git a/mutants/mutant-3/swen90006/machine/BugException.java b/mutants/mutant-3/swen90006/machine/BugException.java new file mode 100644 index 0000000000000000000000000000000000000000..41cb9cab9f4bb06efde089f190abc13331b00b21 --- /dev/null +++ b/mutants/mutant-3/swen90006/machine/BugException.java @@ -0,0 +1,7 @@ +package swen90006.machine; + +public class BugException extends Exception { + public BugException(String msg){ + super("You triggered one of the security bugs!\n" + msg); + } +} diff --git a/mutants/mutant-3/swen90006/machine/InvalidInstructionException.java b/mutants/mutant-3/swen90006/machine/InvalidInstructionException.java new file mode 100644 index 0000000000000000000000000000000000000000..25e042bfb4bf0519927e165946fa9d03640c45fe --- /dev/null +++ b/mutants/mutant-3/swen90006/machine/InvalidInstructionException.java @@ -0,0 +1,9 @@ +package swen90006.machine; + +public class InvalidInstructionException extends RuntimeException +{ + public String toString() + { + return "InvalidInstructionException"; + } +} diff --git a/mutants/mutant-3/swen90006/machine/Machine.java b/mutants/mutant-3/swen90006/machine/Machine.java new file mode 100644 index 0000000000000000000000000000000000000000..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 --- /dev/null +++ b/mutants/mutant-3/swen90006/machine/Machine.java @@ -0,0 +1,303 @@ +package swen90006.machine; + +import java.util.Arrays; +import java.util.List; + +public class Machine +{ + /** arithmetic instructions each take three registers as arguments with the + * destination register appearing first + */ + + /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ + public static final String INSTRUCTION_ADD = "add"; + + /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ + public static final String INSTRUCTION_SUBTRACT = "sub"; + + /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ + public static final String INSTRUCTION_MULT = "mul"; + + /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ + public static final String INSTRUCTION_DIVIDE = "div"; + + /** ret rs =~ return rs */ + public static final String INSTRUCTION_RETURN = "ret"; + + /** ldr rd rs offs =~ rd = rs[offs] */ + public static final String INSTRUCTION_LOAD = "ldr"; + + /** str ra offs rb =~ ra[offs] = rb */ + public static final String INSTRUCTION_STORE = "str"; + + /** mov rd val =~ rd = val */ + public static final String INSTRUCTION_MOVE = "mov"; + + /** jmp offs =~ pc = pc + offs */ + public static final String INSTRUCTION_JUMP = "jmp"; + + /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ + public static final String INSTRUCTION_JZ = "jz"; + + public static final int NUM_REGS = 32; + public static final int MAX_REG = (NUM_REGS - 1); + public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ + public static final int MAX_ADDR = MEMORY_SIZE-1; + + private int[] memory; + private int[] regs; + + private int count = 0; /* counts number of instructions executed so far */ + + public Machine() + { + memory = new int[MEMORY_SIZE]; + regs = new int[NUM_REGS]; + count = 0; + } + + private void do_add(int dest, int src1, int src2) + { + regs[dest] = regs[src1] + regs[src2]; + } + + private void do_sub(int dest, int src1, int src2) + { + regs[dest] = regs[src1] - regs[src2]; + } + + private void do_mult(int dest, int src1, int src2) + { + regs[dest] = regs[src1] * regs[src2]; + } + + private void do_div(int dest, int src1, int src2) + { + if (regs[src2] == 0){ + /* no op */ + }else{ + regs[dest] = regs[src1] / regs[src2]; + } + } + + private void do_load(int dest, int src, int offs) { + if (regs[src] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[src] + offs < 0){ + /* no op */ + }else{ + regs[dest] = memory[regs[src] + offs]; + } + } + + private void do_store(int a, int offs, int b) { + if (regs[a] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[a] + offs < 0){ + /* no op */ + }else{ + memory[regs[a] + offs] = regs[b]; + } + } + + private void do_move(int rd, int val){ + regs[rd] = val; + } + + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); + } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); + } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_reg(num); + return num; + } + + private int parseOffset(String s) + throws InvalidInstructionException + { + int num = 0; + try { + num = Integer.parseInt(s); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_offset(num); + return num; + } + + + private void validate_reg(int reg) + throws InvalidInstructionException + { + if (reg < 0 || reg > MAX_REG) { + throw new InvalidInstructionException(); + } + } + + private void validate_offset(int offset) + throws InvalidInstructionException + { + if (offset < -MAX_ADDR || offset > MAX_ADDR) { + throw new InvalidInstructionException(); + } + } + + /** Execute an assembly program. + * + * @param prog is the program to execute as an iterable collection of strings, + * each of which is a single instruction. + * @return the program's return value. + * @throws Exception when program has unrecognised or + * invalid instructions, or when it returns no result when it finishes + */ + int execute(List<String> instructions) + throws InvalidInstructionException, + NoReturnValueException + { + + int instructionsExecuted = 0; + int pc = 0; + final int progLength = instructions.size(); + while(true){ + if (pc < 0 || pc >= progLength){ + /* will cause NoReturnValueException to be thrown + * but that is not a bug and and indeed is what the + * VM is supposed to do if the pc becomes negative, + * since in this case the program's execution + * finishes early without a return value having + * been produced. */ + break; + } + String inst = instructions.get(pc); + /* strip leading and trailing whitespace */ + inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); + /* strip out any comments */ + String[] toks = inst.split(";"); + inst = toks[0]; + + /* check for blank lines */ + if (inst.equals("")){ + pc = pc + 1; + count++; + continue; + } + + instructionsExecuted++; + /* now tokenize by splitting on whitespace */ + toks = inst.split("\\s+"); + + /* check minimum number of tokens */ + if (toks.length < 2){ + throw new InvalidInstructionException(); + } + + if (toks[0].equals(INSTRUCTION_ADD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_add(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_sub(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_MULT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_mult(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_div(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_RETURN)){ + int rs = parseReg(toks[1]); + count++; + return regs[rs]; + } else if (toks[0].equals(INSTRUCTION_LOAD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs = parseReg(toks[2]); + int offs = parseOffset(toks[3]); + do_load(rd,rs,offs); + } else if (toks[0].equals(INSTRUCTION_STORE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + int rb = parseReg(toks[3]); + do_store(ra,offs,rb); + } else if (toks[0].equals(INSTRUCTION_MOVE)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + do_move(rd,offs); + } else if (toks[0].equals(INSTRUCTION_JUMP)){ + if (toks.length != 2){ + throw new InvalidInstructionException(); + } + int offs = parseOffset(toks[1]); + pc = pc + offs; + count++; + continue; /* avoid default increment of pc below */ + } else if (toks[0].equals(INSTRUCTION_JZ)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + if (regs[ra] == 0){ + pc = pc + offs; + }else{ + pc = pc + 1; + } + count++; + continue; /* avoid default increment the pc below */ + } else { + System.err.println("Unrecognised instruction: " + inst); + throw new InvalidInstructionException(); + } + count++; + pc = pc + 1; + } + + /* got here without returning already... */ + throw new NoReturnValueException(); + } + + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } +} diff --git a/mutants/mutant-3/swen90006/machine/NoReturnValueException.java b/mutants/mutant-3/swen90006/machine/NoReturnValueException.java new file mode 100644 index 0000000000000000000000000000000000000000..154907e85d6d8fa059da74d8fbfff17f29de28d3 --- /dev/null +++ b/mutants/mutant-3/swen90006/machine/NoReturnValueException.java @@ -0,0 +1,11 @@ +package swen90006.machine; + +public class NoReturnValueException extends RuntimeException +{ + public NoReturnValueException() {} + + public String toString() + { + return "NoReturnValueException"; + } +} diff --git a/mutants/mutant-3/swen90006/machine/SimpleDriver.java b/mutants/mutant-3/swen90006/machine/SimpleDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..bf288a4d2d3262d62316a7fa8adeb2a9f9a1dd6e --- /dev/null +++ b/mutants/mutant-3/swen90006/machine/SimpleDriver.java @@ -0,0 +1,51 @@ +package swen90006.machine; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.util.List; + +public class SimpleDriver { + + private static final int MEMORY_SIZE = Machine.MAX_ADDR+1; + + public static void main(String[] args) throws Exception + { + if (args.length != 1){ + System.err.println("Usage: java SimpleDriver <inputfile>"); + System.err.println("\nMust have 1 argument instead of " + args.length); + System.exit(1); + } + + Charset charset = Charset.forName("UTF-8"); + + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(args[0]), charset); + }catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + + Machine m = null; + try { + System.out.println("There are this many instructions: " + lines.size()); + + m = new Machine(); + + Integer res = null; + res = m.execute(lines); + System.out.println("Program result: " + res + "\n"); + + } catch (Exception e) { + System.err.println("Exception while executing program. (stacktrace follows)"); + e.printStackTrace(System.err); + if (m != null){ + System.err.println("Number of instructions executed before exception: " + m.getCount()); + } + System.exit(1); + } + } +} diff --git a/mutants/mutant-4/swen90006/machine/BugException.java b/mutants/mutant-4/swen90006/machine/BugException.java new file mode 100644 index 0000000000000000000000000000000000000000..41cb9cab9f4bb06efde089f190abc13331b00b21 --- /dev/null +++ b/mutants/mutant-4/swen90006/machine/BugException.java @@ -0,0 +1,7 @@ +package swen90006.machine; + +public class BugException extends Exception { + public BugException(String msg){ + super("You triggered one of the security bugs!\n" + msg); + } +} diff --git a/mutants/mutant-4/swen90006/machine/InvalidInstructionException.java b/mutants/mutant-4/swen90006/machine/InvalidInstructionException.java new file mode 100644 index 0000000000000000000000000000000000000000..25e042bfb4bf0519927e165946fa9d03640c45fe --- /dev/null +++ b/mutants/mutant-4/swen90006/machine/InvalidInstructionException.java @@ -0,0 +1,9 @@ +package swen90006.machine; + +public class InvalidInstructionException extends RuntimeException +{ + public String toString() + { + return "InvalidInstructionException"; + } +} diff --git a/mutants/mutant-4/swen90006/machine/Machine.java b/mutants/mutant-4/swen90006/machine/Machine.java new file mode 100644 index 0000000000000000000000000000000000000000..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 --- /dev/null +++ b/mutants/mutant-4/swen90006/machine/Machine.java @@ -0,0 +1,303 @@ +package swen90006.machine; + +import java.util.Arrays; +import java.util.List; + +public class Machine +{ + /** arithmetic instructions each take three registers as arguments with the + * destination register appearing first + */ + + /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ + public static final String INSTRUCTION_ADD = "add"; + + /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ + public static final String INSTRUCTION_SUBTRACT = "sub"; + + /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ + public static final String INSTRUCTION_MULT = "mul"; + + /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ + public static final String INSTRUCTION_DIVIDE = "div"; + + /** ret rs =~ return rs */ + public static final String INSTRUCTION_RETURN = "ret"; + + /** ldr rd rs offs =~ rd = rs[offs] */ + public static final String INSTRUCTION_LOAD = "ldr"; + + /** str ra offs rb =~ ra[offs] = rb */ + public static final String INSTRUCTION_STORE = "str"; + + /** mov rd val =~ rd = val */ + public static final String INSTRUCTION_MOVE = "mov"; + + /** jmp offs =~ pc = pc + offs */ + public static final String INSTRUCTION_JUMP = "jmp"; + + /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ + public static final String INSTRUCTION_JZ = "jz"; + + public static final int NUM_REGS = 32; + public static final int MAX_REG = (NUM_REGS - 1); + public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ + public static final int MAX_ADDR = MEMORY_SIZE-1; + + private int[] memory; + private int[] regs; + + private int count = 0; /* counts number of instructions executed so far */ + + public Machine() + { + memory = new int[MEMORY_SIZE]; + regs = new int[NUM_REGS]; + count = 0; + } + + private void do_add(int dest, int src1, int src2) + { + regs[dest] = regs[src1] + regs[src2]; + } + + private void do_sub(int dest, int src1, int src2) + { + regs[dest] = regs[src1] - regs[src2]; + } + + private void do_mult(int dest, int src1, int src2) + { + regs[dest] = regs[src1] * regs[src2]; + } + + private void do_div(int dest, int src1, int src2) + { + if (regs[src2] == 0){ + /* no op */ + }else{ + regs[dest] = regs[src1] / regs[src2]; + } + } + + private void do_load(int dest, int src, int offs) { + if (regs[src] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[src] + offs < 0){ + /* no op */ + }else{ + regs[dest] = memory[regs[src] + offs]; + } + } + + private void do_store(int a, int offs, int b) { + if (regs[a] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[a] + offs < 0){ + /* no op */ + }else{ + memory[regs[a] + offs] = regs[b]; + } + } + + private void do_move(int rd, int val){ + regs[rd] = val; + } + + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); + } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); + } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_reg(num); + return num; + } + + private int parseOffset(String s) + throws InvalidInstructionException + { + int num = 0; + try { + num = Integer.parseInt(s); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_offset(num); + return num; + } + + + private void validate_reg(int reg) + throws InvalidInstructionException + { + if (reg < 0 || reg > MAX_REG) { + throw new InvalidInstructionException(); + } + } + + private void validate_offset(int offset) + throws InvalidInstructionException + { + if (offset < -MAX_ADDR || offset > MAX_ADDR) { + throw new InvalidInstructionException(); + } + } + + /** Execute an assembly program. + * + * @param prog is the program to execute as an iterable collection of strings, + * each of which is a single instruction. + * @return the program's return value. + * @throws Exception when program has unrecognised or + * invalid instructions, or when it returns no result when it finishes + */ + int execute(List<String> instructions) + throws InvalidInstructionException, + NoReturnValueException + { + + int instructionsExecuted = 0; + int pc = 0; + final int progLength = instructions.size(); + while(true){ + if (pc < 0 || pc >= progLength){ + /* will cause NoReturnValueException to be thrown + * but that is not a bug and and indeed is what the + * VM is supposed to do if the pc becomes negative, + * since in this case the program's execution + * finishes early without a return value having + * been produced. */ + break; + } + String inst = instructions.get(pc); + /* strip leading and trailing whitespace */ + inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); + /* strip out any comments */ + String[] toks = inst.split(";"); + inst = toks[0]; + + /* check for blank lines */ + if (inst.equals("")){ + pc = pc + 1; + count++; + continue; + } + + instructionsExecuted++; + /* now tokenize by splitting on whitespace */ + toks = inst.split("\\s+"); + + /* check minimum number of tokens */ + if (toks.length < 2){ + throw new InvalidInstructionException(); + } + + if (toks[0].equals(INSTRUCTION_ADD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_add(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_sub(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_MULT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_mult(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_div(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_RETURN)){ + int rs = parseReg(toks[1]); + count++; + return regs[rs]; + } else if (toks[0].equals(INSTRUCTION_LOAD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs = parseReg(toks[2]); + int offs = parseOffset(toks[3]); + do_load(rd,rs,offs); + } else if (toks[0].equals(INSTRUCTION_STORE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + int rb = parseReg(toks[3]); + do_store(ra,offs,rb); + } else if (toks[0].equals(INSTRUCTION_MOVE)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + do_move(rd,offs); + } else if (toks[0].equals(INSTRUCTION_JUMP)){ + if (toks.length != 2){ + throw new InvalidInstructionException(); + } + int offs = parseOffset(toks[1]); + pc = pc + offs; + count++; + continue; /* avoid default increment of pc below */ + } else if (toks[0].equals(INSTRUCTION_JZ)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + if (regs[ra] == 0){ + pc = pc + offs; + }else{ + pc = pc + 1; + } + count++; + continue; /* avoid default increment the pc below */ + } else { + System.err.println("Unrecognised instruction: " + inst); + throw new InvalidInstructionException(); + } + count++; + pc = pc + 1; + } + + /* got here without returning already... */ + throw new NoReturnValueException(); + } + + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } +} diff --git a/mutants/mutant-4/swen90006/machine/NoReturnValueException.java b/mutants/mutant-4/swen90006/machine/NoReturnValueException.java new file mode 100644 index 0000000000000000000000000000000000000000..154907e85d6d8fa059da74d8fbfff17f29de28d3 --- /dev/null +++ b/mutants/mutant-4/swen90006/machine/NoReturnValueException.java @@ -0,0 +1,11 @@ +package swen90006.machine; + +public class NoReturnValueException extends RuntimeException +{ + public NoReturnValueException() {} + + public String toString() + { + return "NoReturnValueException"; + } +} diff --git a/mutants/mutant-4/swen90006/machine/SimpleDriver.java b/mutants/mutant-4/swen90006/machine/SimpleDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..bf288a4d2d3262d62316a7fa8adeb2a9f9a1dd6e --- /dev/null +++ b/mutants/mutant-4/swen90006/machine/SimpleDriver.java @@ -0,0 +1,51 @@ +package swen90006.machine; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.util.List; + +public class SimpleDriver { + + private static final int MEMORY_SIZE = Machine.MAX_ADDR+1; + + public static void main(String[] args) throws Exception + { + if (args.length != 1){ + System.err.println("Usage: java SimpleDriver <inputfile>"); + System.err.println("\nMust have 1 argument instead of " + args.length); + System.exit(1); + } + + Charset charset = Charset.forName("UTF-8"); + + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(args[0]), charset); + }catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + + Machine m = null; + try { + System.out.println("There are this many instructions: " + lines.size()); + + m = new Machine(); + + Integer res = null; + res = m.execute(lines); + System.out.println("Program result: " + res + "\n"); + + } catch (Exception e) { + System.err.println("Exception while executing program. (stacktrace follows)"); + e.printStackTrace(System.err); + if (m != null){ + System.err.println("Number of instructions executed before exception: " + m.getCount()); + } + System.exit(1); + } + } +} diff --git a/mutants/mutant-5/swen90006/machine/BugException.java b/mutants/mutant-5/swen90006/machine/BugException.java new file mode 100644 index 0000000000000000000000000000000000000000..41cb9cab9f4bb06efde089f190abc13331b00b21 --- /dev/null +++ b/mutants/mutant-5/swen90006/machine/BugException.java @@ -0,0 +1,7 @@ +package swen90006.machine; + +public class BugException extends Exception { + public BugException(String msg){ + super("You triggered one of the security bugs!\n" + msg); + } +} diff --git a/mutants/mutant-5/swen90006/machine/InvalidInstructionException.java b/mutants/mutant-5/swen90006/machine/InvalidInstructionException.java new file mode 100644 index 0000000000000000000000000000000000000000..25e042bfb4bf0519927e165946fa9d03640c45fe --- /dev/null +++ b/mutants/mutant-5/swen90006/machine/InvalidInstructionException.java @@ -0,0 +1,9 @@ +package swen90006.machine; + +public class InvalidInstructionException extends RuntimeException +{ + public String toString() + { + return "InvalidInstructionException"; + } +} diff --git a/mutants/mutant-5/swen90006/machine/Machine.java b/mutants/mutant-5/swen90006/machine/Machine.java new file mode 100644 index 0000000000000000000000000000000000000000..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 --- /dev/null +++ b/mutants/mutant-5/swen90006/machine/Machine.java @@ -0,0 +1,303 @@ +package swen90006.machine; + +import java.util.Arrays; +import java.util.List; + +public class Machine +{ + /** arithmetic instructions each take three registers as arguments with the + * destination register appearing first + */ + + /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ + public static final String INSTRUCTION_ADD = "add"; + + /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ + public static final String INSTRUCTION_SUBTRACT = "sub"; + + /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ + public static final String INSTRUCTION_MULT = "mul"; + + /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ + public static final String INSTRUCTION_DIVIDE = "div"; + + /** ret rs =~ return rs */ + public static final String INSTRUCTION_RETURN = "ret"; + + /** ldr rd rs offs =~ rd = rs[offs] */ + public static final String INSTRUCTION_LOAD = "ldr"; + + /** str ra offs rb =~ ra[offs] = rb */ + public static final String INSTRUCTION_STORE = "str"; + + /** mov rd val =~ rd = val */ + public static final String INSTRUCTION_MOVE = "mov"; + + /** jmp offs =~ pc = pc + offs */ + public static final String INSTRUCTION_JUMP = "jmp"; + + /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ + public static final String INSTRUCTION_JZ = "jz"; + + public static final int NUM_REGS = 32; + public static final int MAX_REG = (NUM_REGS - 1); + public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ + public static final int MAX_ADDR = MEMORY_SIZE-1; + + private int[] memory; + private int[] regs; + + private int count = 0; /* counts number of instructions executed so far */ + + public Machine() + { + memory = new int[MEMORY_SIZE]; + regs = new int[NUM_REGS]; + count = 0; + } + + private void do_add(int dest, int src1, int src2) + { + regs[dest] = regs[src1] + regs[src2]; + } + + private void do_sub(int dest, int src1, int src2) + { + regs[dest] = regs[src1] - regs[src2]; + } + + private void do_mult(int dest, int src1, int src2) + { + regs[dest] = regs[src1] * regs[src2]; + } + + private void do_div(int dest, int src1, int src2) + { + if (regs[src2] == 0){ + /* no op */ + }else{ + regs[dest] = regs[src1] / regs[src2]; + } + } + + private void do_load(int dest, int src, int offs) { + if (regs[src] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[src] + offs < 0){ + /* no op */ + }else{ + regs[dest] = memory[regs[src] + offs]; + } + } + + private void do_store(int a, int offs, int b) { + if (regs[a] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[a] + offs < 0){ + /* no op */ + }else{ + memory[regs[a] + offs] = regs[b]; + } + } + + private void do_move(int rd, int val){ + regs[rd] = val; + } + + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); + } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); + } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_reg(num); + return num; + } + + private int parseOffset(String s) + throws InvalidInstructionException + { + int num = 0; + try { + num = Integer.parseInt(s); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_offset(num); + return num; + } + + + private void validate_reg(int reg) + throws InvalidInstructionException + { + if (reg < 0 || reg > MAX_REG) { + throw new InvalidInstructionException(); + } + } + + private void validate_offset(int offset) + throws InvalidInstructionException + { + if (offset < -MAX_ADDR || offset > MAX_ADDR) { + throw new InvalidInstructionException(); + } + } + + /** Execute an assembly program. + * + * @param prog is the program to execute as an iterable collection of strings, + * each of which is a single instruction. + * @return the program's return value. + * @throws Exception when program has unrecognised or + * invalid instructions, or when it returns no result when it finishes + */ + int execute(List<String> instructions) + throws InvalidInstructionException, + NoReturnValueException + { + + int instructionsExecuted = 0; + int pc = 0; + final int progLength = instructions.size(); + while(true){ + if (pc < 0 || pc >= progLength){ + /* will cause NoReturnValueException to be thrown + * but that is not a bug and and indeed is what the + * VM is supposed to do if the pc becomes negative, + * since in this case the program's execution + * finishes early without a return value having + * been produced. */ + break; + } + String inst = instructions.get(pc); + /* strip leading and trailing whitespace */ + inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); + /* strip out any comments */ + String[] toks = inst.split(";"); + inst = toks[0]; + + /* check for blank lines */ + if (inst.equals("")){ + pc = pc + 1; + count++; + continue; + } + + instructionsExecuted++; + /* now tokenize by splitting on whitespace */ + toks = inst.split("\\s+"); + + /* check minimum number of tokens */ + if (toks.length < 2){ + throw new InvalidInstructionException(); + } + + if (toks[0].equals(INSTRUCTION_ADD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_add(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_sub(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_MULT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_mult(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_div(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_RETURN)){ + int rs = parseReg(toks[1]); + count++; + return regs[rs]; + } else if (toks[0].equals(INSTRUCTION_LOAD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs = parseReg(toks[2]); + int offs = parseOffset(toks[3]); + do_load(rd,rs,offs); + } else if (toks[0].equals(INSTRUCTION_STORE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + int rb = parseReg(toks[3]); + do_store(ra,offs,rb); + } else if (toks[0].equals(INSTRUCTION_MOVE)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + do_move(rd,offs); + } else if (toks[0].equals(INSTRUCTION_JUMP)){ + if (toks.length != 2){ + throw new InvalidInstructionException(); + } + int offs = parseOffset(toks[1]); + pc = pc + offs; + count++; + continue; /* avoid default increment of pc below */ + } else if (toks[0].equals(INSTRUCTION_JZ)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + if (regs[ra] == 0){ + pc = pc + offs; + }else{ + pc = pc + 1; + } + count++; + continue; /* avoid default increment the pc below */ + } else { + System.err.println("Unrecognised instruction: " + inst); + throw new InvalidInstructionException(); + } + count++; + pc = pc + 1; + } + + /* got here without returning already... */ + throw new NoReturnValueException(); + } + + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } +} diff --git a/mutants/mutant-5/swen90006/machine/NoReturnValueException.java b/mutants/mutant-5/swen90006/machine/NoReturnValueException.java new file mode 100644 index 0000000000000000000000000000000000000000..154907e85d6d8fa059da74d8fbfff17f29de28d3 --- /dev/null +++ b/mutants/mutant-5/swen90006/machine/NoReturnValueException.java @@ -0,0 +1,11 @@ +package swen90006.machine; + +public class NoReturnValueException extends RuntimeException +{ + public NoReturnValueException() {} + + public String toString() + { + return "NoReturnValueException"; + } +} diff --git a/mutants/mutant-5/swen90006/machine/SimpleDriver.java b/mutants/mutant-5/swen90006/machine/SimpleDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..bf288a4d2d3262d62316a7fa8adeb2a9f9a1dd6e --- /dev/null +++ b/mutants/mutant-5/swen90006/machine/SimpleDriver.java @@ -0,0 +1,51 @@ +package swen90006.machine; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.util.List; + +public class SimpleDriver { + + private static final int MEMORY_SIZE = Machine.MAX_ADDR+1; + + public static void main(String[] args) throws Exception + { + if (args.length != 1){ + System.err.println("Usage: java SimpleDriver <inputfile>"); + System.err.println("\nMust have 1 argument instead of " + args.length); + System.exit(1); + } + + Charset charset = Charset.forName("UTF-8"); + + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(args[0]), charset); + }catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + + Machine m = null; + try { + System.out.println("There are this many instructions: " + lines.size()); + + m = new Machine(); + + Integer res = null; + res = m.execute(lines); + System.out.println("Program result: " + res + "\n"); + + } catch (Exception e) { + System.err.println("Exception while executing program. (stacktrace follows)"); + e.printStackTrace(System.err); + if (m != null){ + System.err.println("Number of instructions executed before exception: " + m.getCount()); + } + System.exit(1); + } + } +} diff --git a/src/swen90006/machine/BugException.java b/src/swen90006/machine/BugException.java new file mode 100644 index 0000000000000000000000000000000000000000..41cb9cab9f4bb06efde089f190abc13331b00b21 --- /dev/null +++ b/src/swen90006/machine/BugException.java @@ -0,0 +1,7 @@ +package swen90006.machine; + +public class BugException extends Exception { + public BugException(String msg){ + super("You triggered one of the security bugs!\n" + msg); + } +} diff --git a/src/swen90006/machine/InvalidInstructionException.java b/src/swen90006/machine/InvalidInstructionException.java new file mode 100644 index 0000000000000000000000000000000000000000..25e042bfb4bf0519927e165946fa9d03640c45fe --- /dev/null +++ b/src/swen90006/machine/InvalidInstructionException.java @@ -0,0 +1,9 @@ +package swen90006.machine; + +public class InvalidInstructionException extends RuntimeException +{ + public String toString() + { + return "InvalidInstructionException"; + } +} diff --git a/src/swen90006/machine/Machine.java b/src/swen90006/machine/Machine.java new file mode 100644 index 0000000000000000000000000000000000000000..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 --- /dev/null +++ b/src/swen90006/machine/Machine.java @@ -0,0 +1,303 @@ +package swen90006.machine; + +import java.util.Arrays; +import java.util.List; + +public class Machine +{ + /** arithmetic instructions each take three registers as arguments with the + * destination register appearing first + */ + + /** add rd rs1 rs2 =~ rd = rs1 + rs2 */ + public static final String INSTRUCTION_ADD = "add"; + + /** sub rd rs1 rs2 =~ rd = rs1 - rs2 */ + public static final String INSTRUCTION_SUBTRACT = "sub"; + + /** mul rd rs1 rs2 =~ rd = rs1 * rs2 */ + public static final String INSTRUCTION_MULT = "mul"; + + /** div rd rs1 rs2 =~ rd = rs1 / rs2 */ + public static final String INSTRUCTION_DIVIDE = "div"; + + /** ret rs =~ return rs */ + public static final String INSTRUCTION_RETURN = "ret"; + + /** ldr rd rs offs =~ rd = rs[offs] */ + public static final String INSTRUCTION_LOAD = "ldr"; + + /** str ra offs rb =~ ra[offs] = rb */ + public static final String INSTRUCTION_STORE = "str"; + + /** mov rd val =~ rd = val */ + public static final String INSTRUCTION_MOVE = "mov"; + + /** jmp offs =~ pc = pc + offs */ + public static final String INSTRUCTION_JUMP = "jmp"; + + /** jz ra offs =~ if (ra == 0) pc = pc + offs else pc = pc + 1 */ + public static final String INSTRUCTION_JZ = "jz"; + + public static final int NUM_REGS = 32; + public static final int MAX_REG = (NUM_REGS - 1); + public static final int MEMORY_SIZE = 65536; /* 4 x as much memory as a 64 */ + public static final int MAX_ADDR = MEMORY_SIZE-1; + + private int[] memory; + private int[] regs; + + private int count = 0; /* counts number of instructions executed so far */ + + public Machine() + { + memory = new int[MEMORY_SIZE]; + regs = new int[NUM_REGS]; + count = 0; + } + + private void do_add(int dest, int src1, int src2) + { + regs[dest] = regs[src1] + regs[src2]; + } + + private void do_sub(int dest, int src1, int src2) + { + regs[dest] = regs[src1] - regs[src2]; + } + + private void do_mult(int dest, int src1, int src2) + { + regs[dest] = regs[src1] * regs[src2]; + } + + private void do_div(int dest, int src1, int src2) + { + if (regs[src2] == 0){ + /* no op */ + }else{ + regs[dest] = regs[src1] / regs[src2]; + } + } + + private void do_load(int dest, int src, int offs) { + if (regs[src] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[src] + offs < 0){ + /* no op */ + }else{ + regs[dest] = memory[regs[src] + offs]; + } + } + + private void do_store(int a, int offs, int b) { + if (regs[a] + offs > MAX_ADDR){ + /* no op */ + }else if(regs[a] + offs < 0){ + /* no op */ + }else{ + memory[regs[a] + offs] = regs[b]; + } + } + + private void do_move(int rd, int val){ + regs[rd] = val; + } + + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); + } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); + } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_reg(num); + return num; + } + + private int parseOffset(String s) + throws InvalidInstructionException + { + int num = 0; + try { + num = Integer.parseInt(s); + } catch (Exception e){ + throw new InvalidInstructionException(); + } + validate_offset(num); + return num; + } + + + private void validate_reg(int reg) + throws InvalidInstructionException + { + if (reg < 0 || reg > MAX_REG) { + throw new InvalidInstructionException(); + } + } + + private void validate_offset(int offset) + throws InvalidInstructionException + { + if (offset < -MAX_ADDR || offset > MAX_ADDR) { + throw new InvalidInstructionException(); + } + } + + /** Execute an assembly program. + * + * @param prog is the program to execute as an iterable collection of strings, + * each of which is a single instruction. + * @return the program's return value. + * @throws Exception when program has unrecognised or + * invalid instructions, or when it returns no result when it finishes + */ + int execute(List<String> instructions) + throws InvalidInstructionException, + NoReturnValueException + { + + int instructionsExecuted = 0; + int pc = 0; + final int progLength = instructions.size(); + while(true){ + if (pc < 0 || pc >= progLength){ + /* will cause NoReturnValueException to be thrown + * but that is not a bug and and indeed is what the + * VM is supposed to do if the pc becomes negative, + * since in this case the program's execution + * finishes early without a return value having + * been produced. */ + break; + } + String inst = instructions.get(pc); + /* strip leading and trailing whitespace */ + inst = inst.toLowerCase().replaceAll("^\\s+","").replaceAll("\\s+$",""); + /* strip out any comments */ + String[] toks = inst.split(";"); + inst = toks[0]; + + /* check for blank lines */ + if (inst.equals("")){ + pc = pc + 1; + count++; + continue; + } + + instructionsExecuted++; + /* now tokenize by splitting on whitespace */ + toks = inst.split("\\s+"); + + /* check minimum number of tokens */ + if (toks.length < 2){ + throw new InvalidInstructionException(); + } + + if (toks[0].equals(INSTRUCTION_ADD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_add(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_SUBTRACT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_sub(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_MULT)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_mult(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_DIVIDE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs1 = parseReg(toks[2]); + int rs2 = parseReg(toks[3]); + do_div(rd,rs1,rs2); + } else if (toks[0].equals(INSTRUCTION_RETURN)){ + int rs = parseReg(toks[1]); + count++; + return regs[rs]; + } else if (toks[0].equals(INSTRUCTION_LOAD)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int rs = parseReg(toks[2]); + int offs = parseOffset(toks[3]); + do_load(rd,rs,offs); + } else if (toks[0].equals(INSTRUCTION_STORE)){ + if (toks.length != 4){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + int rb = parseReg(toks[3]); + do_store(ra,offs,rb); + } else if (toks[0].equals(INSTRUCTION_MOVE)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int rd = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + do_move(rd,offs); + } else if (toks[0].equals(INSTRUCTION_JUMP)){ + if (toks.length != 2){ + throw new InvalidInstructionException(); + } + int offs = parseOffset(toks[1]); + pc = pc + offs; + count++; + continue; /* avoid default increment of pc below */ + } else if (toks[0].equals(INSTRUCTION_JZ)){ + if (toks.length != 3){ + throw new InvalidInstructionException(); + } + int ra = parseReg(toks[1]); + int offs = parseOffset(toks[2]); + if (regs[ra] == 0){ + pc = pc + offs; + }else{ + pc = pc + 1; + } + count++; + continue; /* avoid default increment the pc below */ + } else { + System.err.println("Unrecognised instruction: " + inst); + throw new InvalidInstructionException(); + } + count++; + pc = pc + 1; + } + + /* got here without returning already... */ + throw new NoReturnValueException(); + } + + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } +} diff --git a/src/swen90006/machine/NoReturnValueException.java b/src/swen90006/machine/NoReturnValueException.java new file mode 100644 index 0000000000000000000000000000000000000000..154907e85d6d8fa059da74d8fbfff17f29de28d3 --- /dev/null +++ b/src/swen90006/machine/NoReturnValueException.java @@ -0,0 +1,11 @@ +package swen90006.machine; + +public class NoReturnValueException extends RuntimeException +{ + public NoReturnValueException() {} + + public String toString() + { + return "NoReturnValueException"; + } +} diff --git a/src/swen90006/machine/SimpleDriver.java b/src/swen90006/machine/SimpleDriver.java new file mode 100644 index 0000000000000000000000000000000000000000..bf288a4d2d3262d62316a7fa8adeb2a9f9a1dd6e --- /dev/null +++ b/src/swen90006/machine/SimpleDriver.java @@ -0,0 +1,51 @@ +package swen90006.machine; + +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; +import java.util.List; + +public class SimpleDriver { + + private static final int MEMORY_SIZE = Machine.MAX_ADDR+1; + + public static void main(String[] args) throws Exception + { + if (args.length != 1){ + System.err.println("Usage: java SimpleDriver <inputfile>"); + System.err.println("\nMust have 1 argument instead of " + args.length); + System.exit(1); + } + + Charset charset = Charset.forName("UTF-8"); + + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(args[0]), charset); + }catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + + Machine m = null; + try { + System.out.println("There are this many instructions: " + lines.size()); + + m = new Machine(); + + Integer res = null; + res = m.execute(lines); + System.out.println("Program result: " + res + "\n"); + + } catch (Exception e) { + System.err.println("Exception while executing program. (stacktrace follows)"); + e.printStackTrace(System.err); + if (m != null){ + System.err.println("Number of instructions executed before exception: " + m.getCount()); + } + System.exit(1); + } + } +} diff --git a/test/swen90006/machine/BoundaryTests.java b/test/swen90006/machine/BoundaryTests.java new file mode 100644 index 0000000000000000000000000000000000000000..61ce1ca647f325fbf452047408c1e22d9befcb99 --- /dev/null +++ b/test/swen90006/machine/BoundaryTests.java @@ -0,0 +1,90 @@ +package swen90006.machine; + +import java.util.List; +import java.util.ArrayList; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; + +import org.junit.*; +import static org.junit.Assert.*; + +public class BoundaryTests +{ + //Any method annotated with "@Before" will be executed before each test, + //allowing the tester to set up some shared resources. + @Before public void setUp() + { + } + + //Any method annotated with "@After" will be executed after each test, + //allowing the tester to release any shared resources used in the setup. + @After public void tearDown() + { + } + + //Any method annotation with "@Test" is executed as a test. + @Test public void aTest() + { + //the assertEquals method used to check whether two values are + //equal, using the equals method + final int expected = 2; + final int actual = 1 + 1; + assertEquals(expected, actual); + } + + @Test public void anotherTest() + { + List<String> list = new ArrayList<String>(); + list.add("a"); + list.add("b"); + + //the assertTrue method is used to check whether something holds. + assertTrue(list.contains("a")); + } + + //Test test opens a file and executes the machine + @Test public void aFileOpenTest() + { + final List<String> lines = readInstructions("examples/array.s"); + Machine m = new Machine(); + assertEquals(m.execute(lines), 45); + } + + //To test an exception, specify the expected exception after the @Test + @Test(expected = java.io.IOException.class) + public void anExceptionTest() + throws Throwable + { + throw new java.io.IOException(); + } + + //This test should fail. + //To provide additional feedback when a test fails, an error message + //can be included + @Test public void aFailedTest() + { + //include a message for better feedback + final int expected = 2; + final int actual = 1 + 2; + assertEquals("Some failure message", expected, actual); + } + + //Read in a file containing a program and convert into a list of + //string instructions + private List<String> readInstructions(String file) + { + Charset charset = Charset.forName("UTF-8"); + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(file), charset); + } + catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + return lines; + } +} diff --git a/test/swen90006/machine/PartitioningTests.java b/test/swen90006/machine/PartitioningTests.java new file mode 100644 index 0000000000000000000000000000000000000000..5494b44f4615351a610fc7b0b649d30b4b2d0a40 --- /dev/null +++ b/test/swen90006/machine/PartitioningTests.java @@ -0,0 +1,90 @@ +package swen90006.machine; + +import java.util.List; +import java.util.ArrayList; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.nio.file.Files; +import java.nio.file.FileSystems; + +import org.junit.*; +import static org.junit.Assert.*; + +public class PartitioningTests +{ + //Any method annotated with "@Before" will be executed before each test, + //allowing the tester to set up some shared resources. + @Before public void setUp() + { + } + + //Any method annotated with "@After" will be executed after each test, + //allowing the tester to release any shared resources used in the setup. + @After public void tearDown() + { + } + + //Any method annotation with "@Test" is executed as a test. + @Test public void aTest() + { + //the assertEquals method used to check whether two values are + //equal, using the equals method + final int expected = 2; + final int actual = 1 + 1; + assertEquals(expected, actual); + } + + @Test public void anotherTest() + { + List<String> list = new ArrayList<String>(); + list.add("a"); + list.add("b"); + + //the assertTrue method is used to check whether something holds. + assertTrue(list.contains("a")); + } + + //Test test opens a file and executes the machine + @Test public void aFileOpenTest() + { + final List<String> lines = readInstructions("examples/array.s"); + Machine m = new Machine(); + assertEquals(m.execute(lines), 45); + } + + //To test an exception, specify the expected exception after the @Test + @Test(expected = java.io.IOException.class) + public void anExceptionTest() + throws Throwable + { + throw new java.io.IOException(); + } + + //This test should fail. + //To provide additional feedback when a test fails, an error message + //can be included + @Test public void aFailedTest() + { + //include a message for better feedback + final int expected = 2; + final int actual = 1 + 2; + assertEquals("Some failure message", expected, actual); + } + + //Read in a file containing a program and convert into a list of + //string instructions + private List<String> readInstructions(String file) + { + Charset charset = Charset.forName("UTF-8"); + List<String> lines = null; + try { + lines = Files.readAllLines(FileSystems.getDefault().getPath(file), charset); + } + catch (Exception e){ + System.err.println("Invalid input file! (stacktrace follows)"); + e.printStackTrace(System.err); + System.exit(1); + } + return lines; + } +}