protected void runProgram(string program, Dictionary <string, object> variablesIn, List <ISpecFinder> moduleSpecFinders, int expectedIterations, out FrameContext context) { CodeObject compiledProgram = null; try { compiledProgram = ByteCodeCompiler.Compile(program, variablesIn); } catch (CloacaParseException parseFailed) { Assert.Fail(parseFailed.Message); } Dis.dis(compiledProgram); // TODO: This dependency association is kind of gross. It's almost circular and is broken by assigning // the interpreter reference to the schedular after its initial constructor. var scheduler = new Scheduler(); var interpreter = new Interpreter(scheduler); interpreter.DumpState = true; foreach (var finder in moduleSpecFinders) { interpreter.AddModuleFinder(finder); } scheduler.SetInterpreter(interpreter); var receipt = scheduler.Schedule(compiledProgram); context = receipt.Frame; foreach (string varName in variablesIn.Keys) { context.SetVariable(varName, variablesIn[varName]); } // Waiting on the task makes sure we get punched in the face by any exceptions it throws. // But they'll come rolling in as AggregateExceptions so we'll have to unpack them. var scheduler_task = scheduler.RunUntilDone(); scheduler_task.Wait(); Assert.That(receipt.Completed); if (receipt.EscapedExceptionInfo != null) { receipt.EscapedExceptionInfo.Throw(); } Assert.That(scheduler.TickCount, Is.EqualTo(expectedIterations)); }
protected async Task <FrameContext> runProgram(string program, Dictionary <string, object> variablesIn, List <ISpecFinder> moduleSpecFinders, int expectedIterations, bool checkExceptions = true) { // TODO: This dependency association is kind of gross. It's almost circular and is broken by assigning // the interpreter reference to the schedular after its initial constructor. var scheduler = new Scheduler(); var interpreter = new Interpreter(scheduler); interpreter.DumpState = true; foreach (var finder in moduleSpecFinders) { interpreter.AddModuleFinder(finder); } scheduler.SetInterpreter(interpreter); scheduler.OnTaskScheduled += whenTaskScheduled; escapedExceptions = new List <ExceptionDispatchInfo>(); CodeObject compiledProgram = null; Task <CodeObject> compiledTask = null; try { // This is awaitable now but relies on the scheduler. We'll tick the scheduler // awhile until this resolves. compiledTask = ByteCodeCompiler.Compile(program, variablesIn, scheduler); } catch (CloacaParseException parseFailed) { Assert.Fail(parseFailed.Message); } for (int tries = 1; tries < 1000 && !compiledTask.IsCompleted && escapedExceptions.Count == 0; ++tries) { await scheduler.Tick(); } if (!compiledTask.IsCompleted) { Assert.Fail("Compilation did not finish with interpreter after 1,000 scheduler ticks"); } else if (escapedExceptions.Count > 0) { escapedExceptions[0].Throw(); } compiledProgram = await compiledTask; Dis.dis(compiledProgram); receipt = scheduler.Schedule(compiledProgram); FrameContext context = receipt.Frame; foreach (string varName in variablesIn.Keys) { context.SetVariable(varName, variablesIn[varName]); } // Waiting on the task makes sure we get punched in the face by any exceptions it throws. // But they'll come rolling in as AggregateExceptions so we'll have to unpack them. var scheduler_task = scheduler.RunUntilDone(); scheduler_task.Wait(); Assert.That(receipt.Completed); if (checkExceptions) { AssertNoExceptions(); } Assert.That(scheduler.TickCount, Is.EqualTo(expectedIterations)); return(context); }