Skip to content

Commit dcd208c

Browse files
committed
Machine can be limited to exec a number of lines
The machine now takes an optional constructor argument(default 100) that indicates how many lines the machine will execute before giving up and raising an exception. This is useful to trap potential infinite loop situations.
1 parent 9b2c48a commit dcd208c

File tree

4 files changed

+41
-9
lines changed

4 files changed

+41
-9
lines changed

src/lines.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
const MaxInstructionsExceededException = require('./maxInstructionsExceededException.js');
12
const ProgramCounter = require('./programCounter.js');
23

34
class Lines {
4-
constructor() {
5+
constructor(maxLinesToExecute) {
6+
this.maxLinesToExecute = maxLinesToExecute;
57
this.lines = [];
68
this.fnTable = {};
79
}
@@ -16,9 +18,14 @@ class Lines {
1618
let state = { regs, flags, halt: false };
1719
let lineNumbers = this.lines.map(l => l.getLineNumber());
1820
let programCounter = new ProgramCounter(lineNumbers, this.fnTable);
21+
let numberOfLinesExecuted = 0;
1922
let executor = () => {
2023
let line = this.lines[programCounter.getCurrentLineIndex()];
2124
state = line.execute(state.regs, state.flags, stack, programCounter);
25+
numberOfLinesExecuted++;
26+
if (numberOfLinesExecuted > this.maxLinesToExecute) {
27+
throw new MaxInstructionsExceededException(this.maxLinesToExecute);
28+
}
2229
state.nextLine = programCounter.getNextLineNumber();
2330
programCounter.update();
2431
cb(state);

src/machine.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ const Stack = require('./stack.js');
44
const Lines = require('./lines.js');
55

66
class Machine {
7-
constructor() {
8-
this.lines = new Lines();
7+
constructor(maxLinesToExecute = 100) {
8+
this.lines = new Lines(maxLinesToExecute);
99
this.stack = new Stack();
10+
this.maxLinesToExecute = maxLinesToExecute;
1011
this._reset();
1112
}
1213

@@ -27,12 +28,13 @@ class Machine {
2728
}
2829

2930
load(program) {
30-
this.lines = new Lines();
3131
let instructions = program.split(/\n/);
3232
instructions.forEach((instruction, index) => {
3333
let line;
3434
try {
35-
let { lineNumber, command, args, nonExecutableLine } = parse(instruction);
35+
let { lineNumber, command, args, nonExecutableLine } = parse(
36+
instruction
37+
);
3638
if (nonExecutableLine) return;
3739
line = Line.create(lineNumber, command, args, index + 1, instruction);
3840
} catch (e) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class MaxInstructionsExceededException extends Error {
2+
constructor(maxLinesToExecute) {
3+
super();
4+
this.maxLinesToExecute = maxLinesToExecute;
5+
}
6+
}
7+
8+
module.exports = MaxInstructionsExceededException;

test/testMachine.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@ describe('Machine loading', function() {
1717
const program = ['10 STAR'];
1818
assert.throws(() => machine.load(stitch(program)));
1919
});
20-
21-
it('should throw an exception when there is a parse error in the arguments',() => {
20+
21+
it('should throw an exception when there is a parse error in the arguments', () => {
2222
const machine = new Machine();
2323
const missingArgumentProg = ['10 MOV A,'];
2424
const missingQuoteProg = ['10 PRN "HELLO'];
25-
assert.throws(() => machine.load(stitch(missingArgumentProg)), InvalidInstructionException);
26-
assert.throws(() => machine.load(stitch(missingQuoteProg)), InvalidInstructionException);
25+
assert.throws(
26+
() => machine.load(stitch(missingArgumentProg)),
27+
InvalidInstructionException
28+
);
29+
assert.throws(
30+
() => machine.load(stitch(missingQuoteProg)),
31+
InvalidInstructionException
32+
);
2733
});
2834

2935
it('should ignore empty lines', function() {
@@ -692,3 +698,12 @@ describe('Machine with functions', () => {
692698
assert.deepEqual({ A: 30, B: 0, C: 0, D: 0 }, machine.getRegs());
693699
});
694700
});
701+
702+
describe('Machine with infinite loops', () => {
703+
it('should throw an exception when the machine executes more than 10 lines', () => {
704+
const machine = new Machine(10);
705+
const program = ['10 START', '20 JMP 10', '30 STOP'];
706+
machine.load(stitch(program));
707+
assert.throws(() => machine.execute());
708+
});
709+
});

0 commit comments

Comments
 (0)