diff --git a/Assignment_1.pdf b/Assignment_1.pdf deleted file mode 100644 index 4fb53cb99fbf341138490c8411d36a7e910a1f01..0000000000000000000000000000000000000000 Binary files a/Assignment_1.pdf and /dev/null differ diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/build.xml b/build.xml old mode 100644 new mode 100755 diff --git a/examples/array.s b/examples/array.s old mode 100644 new mode 100755 diff --git a/examples/factorial.s b/examples/factorial.s old mode 100644 new mode 100755 diff --git a/lib/hamcrest-core-1.3.jar b/lib/hamcrest-core-1.3.jar old mode 100644 new mode 100755 diff --git a/lib/junit-4.11.jar b/lib/junit-4.11.jar old mode 100644 new mode 100755 diff --git a/machine.iml b/machine.iml new file mode 100644 index 0000000000000000000000000000000000000000..2e47cbd4e7dd649166d50110dd714ddb80275e7c --- /dev/null +++ b/machine.iml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="library" name="lib" level="project" /> + </component> +</module> \ No newline at end of file diff --git a/machine.zip b/machine.zip deleted file mode 100644 index 1d51cbc55e5d55a7f69fea8a60be3eac510b9bd9..0000000000000000000000000000000000000000 Binary files a/machine.zip and /dev/null differ diff --git a/mutants/mutants.iml b/mutants/mutants.iml new file mode 100644 index 0000000000000000000000000000000000000000..bc0c38513118b59e4b1eef4f4a33af51033871ec --- /dev/null +++ b/mutants/mutants.iml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/mutant-1" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mutant-2" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mutant-3" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mutant-4" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/mutant-5" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> \ No newline at end of file diff --git a/out/production/machine/swen90006/machine/BugException.class b/out/production/machine/swen90006/machine/BugException.class new file mode 100644 index 0000000000000000000000000000000000000000..8744488f1d74f9d9b337ff09a0d4bbca5bba1f54 Binary files /dev/null and b/out/production/machine/swen90006/machine/BugException.class differ diff --git a/out/production/machine/swen90006/machine/InvalidInstructionException.class b/out/production/machine/swen90006/machine/InvalidInstructionException.class new file mode 100644 index 0000000000000000000000000000000000000000..8def40ef75a854a703f27f7530c0c2e98a1bf393 Binary files /dev/null and b/out/production/machine/swen90006/machine/InvalidInstructionException.class differ diff --git a/out/production/machine/swen90006/machine/Machine.class b/out/production/machine/swen90006/machine/Machine.class new file mode 100644 index 0000000000000000000000000000000000000000..ba73230d86d8d10f13461f66f14f1c84eadaef3a Binary files /dev/null and b/out/production/machine/swen90006/machine/Machine.class differ diff --git a/out/production/machine/swen90006/machine/NoReturnValueException.class b/out/production/machine/swen90006/machine/NoReturnValueException.class new file mode 100644 index 0000000000000000000000000000000000000000..5358c5d6f3da8d0d5a23aa31321330ea4cdaca73 Binary files /dev/null and b/out/production/machine/swen90006/machine/NoReturnValueException.class differ diff --git a/out/production/machine/swen90006/machine/SimpleDriver.class b/out/production/machine/swen90006/machine/SimpleDriver.class new file mode 100644 index 0000000000000000000000000000000000000000..bf8aa369bef77b919dcdfba37a083e94f2c4b904 Binary files /dev/null and b/out/production/machine/swen90006/machine/SimpleDriver.class differ diff --git a/out/test/machine/swen90006/machine/BoundaryTests.class b/out/test/machine/swen90006/machine/BoundaryTests.class new file mode 100644 index 0000000000000000000000000000000000000000..6c5b485f8ffe86d62989ec9ef7c5cab17995ce2f Binary files /dev/null and b/out/test/machine/swen90006/machine/BoundaryTests.class differ diff --git a/out/test/machine/swen90006/machine/PartitioningTests.class b/out/test/machine/swen90006/machine/PartitioningTests.class new file mode 100644 index 0000000000000000000000000000000000000000..077d92dde3a92846e2add5f5c3a3544ff0d3e96a Binary files /dev/null and b/out/test/machine/swen90006/machine/PartitioningTests.class differ diff --git a/src/swen90006/machine/Machine.java b/src/swen90006/machine/Machine.java index 92d3b56553b579c2419f7ff1245bcee45f1af3f8..9bf57316d1c736ea2bd1c62bd6df3c6c0282f331 100755 --- a/src/swen90006/machine/Machine.java +++ b/src/swen90006/machine/Machine.java @@ -3,301 +3,301 @@ package swen90006.machine; import java.util.Arrays; import java.util.List; -public class Machine +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; + /** 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_add(int dest, int src1, int src2) - { - 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_sub(int dest, int src1, int src2) - { - regs[dest] = regs[src1] - regs[src2]; + } + + 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_mult(int dest, int src1, int src2) - { - regs[dest] = regs[src1] * regs[src2]; - } + private void do_move(int rd, int val){ + regs[rd] = val; + } - private void do_div(int dest, int src1, int src2) - { - if (regs[src2] == 0){ - /* no op */ - }else{ - regs[dest] = regs[src1] / regs[src2]; - } + private int parseReg(String s) throws InvalidInstructionException + { + if (s.length() < 2){ + throw new InvalidInstructionException(); } - - 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]; - } + if (s.charAt(0) != 'r'){ + throw new InvalidInstructionException(); } - - 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]; - } + String numstr = s.substring(1); + int num = 0; + try { + num = Integer.parseInt(numstr); + } catch (Exception e){ + throw new InvalidInstructionException(); } - - private void do_move(int rd, int val){ - regs[rd] = val; + 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 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_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(); } - - 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; } - /** 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]; + /* got here without returning already... */ + throw new NoReturnValueException(); + } - /* 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; - } + /** + * get the number of instructions successfully executed by the VM so far + */ + public int getCount(){ + return count; + } } diff --git a/test/swen90006/machine/BoundaryTests.java b/test/swen90006/machine/BoundaryTests.java index 84bd4c05426f58682183be0d2295e212460185e5..0a1a3929d2a8252b5e69d288d868d1d80941ab92 100755 --- a/test/swen90006/machine/BoundaryTests.java +++ b/test/swen90006/machine/BoundaryTests.java @@ -432,8 +432,6 @@ public class BoundaryTests { } - - //******************************* EC23 ******************************** /* * boundary: val > pc_ && val > 0 @@ -576,4 +574,27 @@ public class BoundaryTests { Machine m = new Machine(); return m.execute(lines); } + + //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(); + } + + + //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; + } } \ No newline at end of file diff --git a/test/swen90006/machine/PartitioningTests.java b/test/swen90006/machine/PartitioningTests.java index 41092c667f213710f4a9d78c892c70a838509538..60618f49f7f43f8ce89776c8f5d7008d378bad04 100755 --- a/test/swen90006/machine/PartitioningTests.java +++ b/test/swen90006/machine/PartitioningTests.java @@ -481,4 +481,30 @@ public class PartitioningTests { Machine m = new Machine(); return m.execute(lines); } + + //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(); + } + + + //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; + } } \ No newline at end of file