private void ProcessCall(ScriptBuilder sb) { if (Arguments.Length < 1 || Arguments.Length > 2) { throw new CompilerException(LineNumber, ERR_INCORRECT_NUMBER); } if (!Arguments[0].IsLabel()) { throw new CompilerException(LineNumber, ERR_INVALID_ARGUMENT); } byte regCount = 0; if (Arguments.Length < 2) { regCount = VirtualMachine.DefaultRegisterCount; } else if (Arguments[1].IsNumber()) { regCount = (byte)Arguments[1].AsNumber(); } else { throw new CompilerException(LineNumber, ERR_INVALID_ARGUMENT); } sb.EmitCall(Arguments[0].AsLabel(), regCount); }
public void TestBreakPointStepOver() { using (var engine = new ExecutionEngine()) using (var script = new ScriptBuilder()) { /* ┌ CALL * │ ┌> NOT * │ │ RET * └>X│ PUSH0 * └┘ RET */ script.EmitCall(4); script.Emit(OpCode.NOT); script.Emit(OpCode.RET); script.Emit(OpCode.PUSH0); script.Emit(OpCode.RET); engine.LoadScript(script.ToArray()); var debugger = new Debugger(engine); Assert.AreEqual(OpCode.NOT, engine.CurrentContext.NextInstruction.OpCode); debugger.AddBreakPoint(engine.CurrentContext.Script, 5); Assert.AreEqual(VMState.BREAK, debugger.StepOver()); Assert.AreEqual(OpCode.RET, engine.CurrentContext.NextInstruction.OpCode); Assert.AreEqual(5, engine.CurrentContext.InstructionPointer); Assert.AreEqual(VMState.BREAK, engine.State); debugger.Execute(); Assert.AreEqual(true, engine.ResultStack.Pop().ToBoolean()); Assert.AreEqual(VMState.HALT, engine.State); } }
public void TestStepInto() { using (var engine = new ExecutionEngine()) using (var script = new ScriptBuilder()) { /* ┌ CALL * │ ┌> NOT * │ │ RET * └> │ PUSH0 * └─┘ RET */ script.EmitCall(4); script.Emit(OpCode.NOT); script.Emit(OpCode.RET); script.Emit(OpCode.PUSH0); script.Emit(OpCode.RET); engine.LoadScript(script.ToArray()); var debugger = new Debugger(engine); var context = engine.CurrentContext; Assert.AreEqual(context, engine.CurrentContext); Assert.AreEqual(context, engine.EntryContext); Assert.AreEqual(OpCode.NOT, engine.CurrentContext.NextInstruction.OpCode); Assert.AreEqual(VMState.BREAK, debugger.StepInto()); Assert.AreNotEqual(context, engine.CurrentContext); Assert.AreEqual(context, engine.EntryContext); Assert.AreEqual(OpCode.RET, engine.CurrentContext.NextInstruction.OpCode); Assert.AreEqual(VMState.BREAK, debugger.StepInto()); Assert.AreEqual(VMState.BREAK, debugger.StepInto()); Assert.AreEqual(context, engine.CurrentContext); Assert.AreEqual(context, engine.EntryContext); Assert.AreEqual(OpCode.RET, engine.CurrentContext.NextInstruction.OpCode); Assert.AreEqual(VMState.BREAK, debugger.StepInto()); Assert.AreEqual(VMState.HALT, debugger.StepInto()); Assert.AreEqual(true, engine.ResultStack.Pop().ToBoolean()); Assert.AreEqual(VMState.HALT, engine.State); // Test step into again Assert.AreEqual(VMState.HALT, debugger.StepInto()); Assert.AreEqual(VMState.HALT, engine.State); } }
public void TestEmitCall() { using (var script = new ScriptBuilder()) { script.EmitCall(0); CollectionAssert.AreEqual(new[] { (byte)OpCode.CALL, (byte)0 }, script.ToArray()); } using (var script = new ScriptBuilder()) { script.EmitCall(12345); CollectionAssert.AreEqual(new[] { (byte)OpCode.CALL_L }.Concat(BitConverter.GetBytes(12345)).ToArray(), script.ToArray()); } using (var script = new ScriptBuilder()) { script.EmitCall(-12345); CollectionAssert.AreEqual(new[] { (byte)OpCode.CALL_L }.Concat(BitConverter.GetBytes(-12345)).ToArray(), script.ToArray()); } }
public void TranslateInstruction(Instruction i) { switch (i.op) { case Instruction.Opcode.Label: { _output.EmitLabel(i.target); break; } case Instruction.Opcode.Push: { var reg = FetchRegister(i.target); _output.EmitPush(reg); break; } case Instruction.Opcode.Pop: { var reg = FetchRegister(i.target); _output.Emit(VM.Opcode.POP, new byte[] { reg }); break; } case Instruction.Opcode.Assign: { if (i.literal != null) { switch (i.literal.kind) { case LiteralKind.String: { var reg = FetchRegister(i.target); _output.EmitLoad(reg, (string)i.literal.value); break; } case LiteralKind.Boolean: { var reg = FetchRegister(i.target); _output.EmitLoad(reg, (bool)i.literal.value); break; } case LiteralKind.Integer: { var reg = FetchRegister(i.target); BigInteger val; if (i.literal.value is BigInteger) { val = (BigInteger)i.literal.value; } else if (i.literal.value is int) { val = new BigInteger((int)i.literal.value); } else { throw new Exception($"Could not convert {i.literal.value.GetType().Name} to BigInteger"); } _output.EmitLoad(reg, val); break; } default: throw new Exception("Unsuported " + i.literal.kind); } } else { var src = i.varName != null?FetchRegister(i.varName) : FetchRegister(i.a.target); var dst = FetchRegister(i.target); _output.EmitMove(src, dst); } break; } case Instruction.Opcode.Add: { InsertOp(i, VM.Opcode.ADD); break; } case Instruction.Opcode.Sub: { InsertOp(i, VM.Opcode.SUB); break; } case Instruction.Opcode.Mul: { InsertOp(i, VM.Opcode.MUL); break; } case Instruction.Opcode.Div: { InsertOp(i, VM.Opcode.DIV); break; } case Instruction.Opcode.Mod: { InsertOp(i, VM.Opcode.MOD); break; } case Instruction.Opcode.Shr: { InsertOp(i, VM.Opcode.SHR); break; } case Instruction.Opcode.Shl: { InsertOp(i, VM.Opcode.SHL); break; } case Instruction.Opcode.Equals: { InsertOp(i, VM.Opcode.EQUAL); break; } case Instruction.Opcode.LessThan: { InsertOp(i, VM.Opcode.LT); break; } case Instruction.Opcode.GreaterThan: { InsertOp(i, VM.Opcode.GT); break; } case Instruction.Opcode.LessOrEqualThan: { InsertOp(i, VM.Opcode.LTE); break; } case Instruction.Opcode.GreaterOrEqualThan: { InsertOp(i, VM.Opcode.GTE); break; } case Instruction.Opcode.Jump: InsertJump(i, VM.Opcode.JMP); break; case Instruction.Opcode.JumpIfFalse: InsertJump(i, VM.Opcode.JMPNOT); break; case Instruction.Opcode.JumpIfTrue: InsertJump(i, VM.Opcode.JMPIF); break; case Instruction.Opcode.Call: _output.EmitCall(i.target, 8); // TODO remove hardcoded register count break; case Instruction.Opcode.Return: _output.Emit(VM.Opcode.RET); break; case Instruction.Opcode.Negate: { var src = FetchRegister(i.a.target); var dst = FetchRegister(i.target); _output.Emit(VM.Opcode.NEGATE, new byte[] { src, dst }); break; } case Instruction.Opcode.Not: { var src = FetchRegister(i.a.target); var dst = FetchRegister(i.target); _output.Emit(VM.Opcode.NOT, new byte[] { src, dst }); break; } default: throw new Exception("Unsupported Opcode: " + i.op); } }