public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, IfStatement ifStatement) { bcc.CompileExpression(parser, buffer, ifStatement.Condition, true); ByteBuffer trueCode = new ByteBuffer(); bcc.Compile(parser, trueCode, ifStatement.TrueCode); ByteBuffer falseCode = new ByteBuffer(); bcc.Compile(parser, falseCode, ifStatement.FalseCode); if (falseCode.Size == 0) { if (trueCode.Size == 0) { buffer.Add(ifStatement.Condition.FirstToken, OpCode.POP); } else { buffer.Add(ifStatement.Condition.FirstToken, OpCode.JUMP_IF_FALSE, trueCode.Size); buffer.Concat(trueCode); } } else { trueCode.Add(null, OpCode.JUMP, falseCode.Size); buffer.Add(ifStatement.Condition.FirstToken, OpCode.JUMP_IF_FALSE, trueCode.Size); buffer.Concat(trueCode); buffer.Concat(falseCode); } }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, ForEachLoop forEachLoop) { bcc.CompileExpression(parser, buffer, forEachLoop.IterationExpression, true); buffer.Add( forEachLoop.IterationExpression.FirstToken, OpCode.VERIFY_TYPE_IS_ITERABLE, forEachLoop.ListLocalId.ID, forEachLoop.IndexLocalId.ID); ByteBuffer body = new ByteBuffer(); ByteBuffer body2 = new ByteBuffer(); bcc.Compile(parser, body2, forEachLoop.Code); body.Add( forEachLoop.FirstToken, OpCode.ITERATION_STEP, body2.Size + 1, forEachLoop.IterationVariableId.ID, forEachLoop.IndexLocalId.ID, forEachLoop.ListLocalId.ID); body2.Add(null, OpCode.JUMP, -body2.Size - 2); body.Concat(body2); body.ResolveBreaks(); body.ResolveContinues(); buffer.Concat(body); }
public async void Execute(string program, Dictionary <string, object> variablesIn) { CodeObject compiledProgram = null; try { compiledProgram = ByteCodeCompiler.Compile(program, variablesIn); } catch (Exception ex) { WriteStdout(ex.Message); return; } var receipt = _scheduler.Schedule(compiledProgram); foreach (var variableName in variablesIn.Keys) { receipt.Frame.SetVariable(variableName, variablesIn[variableName]); } while (!_scheduler.Done) { await _scheduler.Tick(); } if (receipt.Completed) { if (receipt.EscapedExceptionInfo != null) { WriteStdout($"{receipt.EscapedExceptionInfo.SourceException}"); } WriteStdout("Done."); } }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, ForLoop forLoop) { bcc.Compile(parser, buffer, forLoop.Init); ByteBuffer codeBuffer = new ByteBuffer(); bcc.Compile(parser, codeBuffer, forLoop.Code); codeBuffer.ResolveContinues(true); // resolve continues as jump-to-end before you add the step instructions. bcc.Compile(parser, codeBuffer, forLoop.Step); ByteBuffer forBuffer = new ByteBuffer(); bcc.CompileExpression(parser, forBuffer, forLoop.Condition, true); forBuffer.Add(forLoop.Condition.FirstToken, OpCode.JUMP_IF_FALSE, codeBuffer.Size + 1); // +1 to go past the jump I'm about to add. forBuffer.Concat(codeBuffer); forBuffer.Add(null, OpCode.JUMP, -forBuffer.Size - 1); forBuffer.ResolveBreaks(); buffer.Concat(forBuffer); }
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)); }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, DoWhileLoop doWhileLoop) { ByteBuffer loopBody = new ByteBuffer(); bcc.Compile(parser, loopBody, doWhileLoop.Code); loopBody.ResolveContinues(true); // continues should jump to the condition, hence the true. ByteBuffer condition = new ByteBuffer(); bcc.CompileExpression(parser, condition, doWhileLoop.Condition, true); loopBody.Concat(condition); loopBody.Add(doWhileLoop.Condition.FirstToken, OpCode.JUMP_IF_TRUE, -loopBody.Size - 1); loopBody.ResolveBreaks(); buffer.Concat(loopBody); }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, WhileLoop whileLoop) { ByteBuffer loopBody = new ByteBuffer(); bcc.Compile(parser, loopBody, whileLoop.Code); ByteBuffer condition = new ByteBuffer(); bcc.CompileExpression(parser, condition, whileLoop.Condition, true); condition.Add(whileLoop.Condition.FirstToken, OpCode.JUMP_IF_FALSE, loopBody.Size + 1); condition.Concat(loopBody); condition.Add(null, OpCode.JUMP, -condition.Size - 1); condition.ResolveBreaks(); condition.ResolveContinues(); buffer.Concat(condition); }
public static void Compile( ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, FunctionDefinition funDef, bool isMethod) { ByteBuffer tBuffer = new ByteBuffer(); List <int> offsetsForOptionalArgs = new List <int>(); CompileFunctionArgs(bcc, parser, tBuffer, funDef.ArgNames, funDef.DefaultValues, offsetsForOptionalArgs, funDef.Locals); if (funDef.CompilationScope.IsStaticallyTyped) { EncodeArgTypeCheck(tBuffer, funDef, funDef.ResolvedArgTypes); } bcc.Compile(parser, tBuffer, funDef.Code); List <int> args = new List <int>() { funDef.FunctionID, parser.GetId(funDef.NameToken.Value), // local var to save in GetMinArgCountFromDefaultValuesList(funDef.DefaultValues), funDef.ArgNames.Length, // max number of args supplied isMethod ? (funDef.Modifiers.HasStatic ? 2 : 1) : 0, // type (0 - function, 1 - method, 2 - static method) isMethod ? ((ClassDefinition)funDef.Owner).ClassID : 0, funDef.LocalScopeSize, tBuffer.Size, offsetsForOptionalArgs.Count }; args.AddRange(offsetsForOptionalArgs); buffer.Add( funDef.FirstToken, OpCode.FUNCTION_DEFINITION, funDef.NameToken.Value, args.ToArray()); buffer.Concat(tBuffer); AddDebugSymbolData(buffer, parser, funDef); }
public async Task <object> Load(IInterpreter interpreter, FrameContext context, PyModuleSpec spec) { var foundPath = (string)spec.LoaderState; var inFile = File.ReadAllText(foundPath); var moduleCode = ByteCodeCompiler.Compile(inFile, new Dictionary <string, object>()); await interpreter.CallInto(context, moduleCode, new object[0]); if (context.EscapedDotNetException != null) { throw context.EscapedDotNetException; } var moduleFrame = context.callStack.Pop(); var module = PyModule.Create(spec.Name); for (int local_i = 0; local_i < moduleFrame.LocalNames.Count; ++local_i) { module.__setattr__(moduleFrame.LocalNames[local_i], moduleFrame.Locals[local_i]); } return(module); }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, Lambda lambda, bool outputUsed) { ByteCodeCompiler.EnsureUsed(lambda, outputUsed); ByteBuffer tBuffer = new ByteBuffer(); List <int> offsetsForOptionalArgs = new List <int>(); Expression[] argDefaultValues_allRequired = new Expression[lambda.Args.Length]; FunctionDefinitionEncoder.CompileFunctionArgs(bcc, parser, tBuffer, lambda.Args, argDefaultValues_allRequired, offsetsForOptionalArgs, lambda.ArgVarIds); bcc.Compile(parser, tBuffer, lambda.Code); List <int> args = new List <int>() { lambda.Args.Length, // min number of args required lambda.Args.Length, // max number of args supplied lambda.LocalScopeSize, tBuffer.Size, offsetsForOptionalArgs.Count }; args.AddRange(offsetsForOptionalArgs); VariableId[] closureIds = lambda.ClosureIds; args.Add(closureIds.Length); foreach (VariableId closureVarId in closureIds) { args.Add(closureVarId.ClosureID); } buffer.Add( lambda.FirstToken, OpCode.LAMBDA, args.ToArray()); buffer.Concat(tBuffer); }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, ConstructorDefinition constructor, ByteBuffer complexFieldInitializers) { ByteBuffer tBuffer = new ByteBuffer(); ClassDefinition cd = (ClassDefinition)constructor.Owner; List <int> offsetsForOptionalArgs = new List <int>(); FunctionDefinitionEncoder.CompileFunctionArgs(bcc, parser, tBuffer, constructor.ArgNames, constructor.DefaultValues, offsetsForOptionalArgs, constructor.ArgLocalIds); int minArgs = 0; int maxArgs = constructor.ArgNames.Length; for (int i = 0; i < constructor.ArgNames.Length; ++i) { if (constructor.DefaultValues[i] == null) { minArgs++; } else { break; } } if (constructor.BaseToken != null) { bcc.CompileExpressionList(parser, tBuffer, constructor.BaseArgs, true); tBuffer.Add( constructor.BaseToken, OpCode.CALL_FUNCTION, (int)FunctionInvocationType.BASE_CONSTRUCTOR, constructor.BaseArgs.Length, cd.BaseClass.Constructor.FunctionID, 0, cd.BaseClass.ClassID); } if (complexFieldInitializers != null) { tBuffer.Concat(complexFieldInitializers); } bcc.Compile(parser, tBuffer, constructor.Code); tBuffer.Add(null, OpCode.RETURN, 0); List <int> args = new List <int>() { constructor.FunctionID, -1, minArgs, maxArgs, constructor.Modifiers.HasStatic ? 4 : 3, cd.ClassID, constructor.LocalScopeSize, tBuffer.Size, offsetsForOptionalArgs.Count, }; args.AddRange(offsetsForOptionalArgs); buffer.Add(constructor.FirstToken, OpCode.FUNCTION_DEFINITION, "<constructor>", args.ToArray()); buffer.Concat(tBuffer); }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, SwitchStatement switchStatement) { bool isInt = switchStatement.UsesIntegers; bcc.CompileExpression(parser, buffer, switchStatement.Condition, true); ByteBuffer chunkBuffer = new ByteBuffer(); Dictionary <int, int> chunkIdsToOffsets = new Dictionary <int, int>(); Dictionary <int, int> integersToChunkIds = new Dictionary <int, int>(); Dictionary <string, int> stringsToChunkIds = new Dictionary <string, int>(); int defaultChunkId = -1; foreach (SwitchStatement.Chunk chunk in switchStatement.Chunks) { int chunkId = chunk.ID; if (chunk.Cases.Length == 1 && chunk.Cases[0] == null) { defaultChunkId = chunkId; } else { foreach (Expression expression in chunk.Cases) { if (isInt) { integersToChunkIds[((IntegerConstant)expression).Value] = chunkId; } else { stringsToChunkIds[((StringConstant)expression).Value] = chunkId; } } } chunkIdsToOffsets[chunkId] = chunkBuffer.Size; bcc.Compile(parser, chunkBuffer, chunk.Code); } chunkBuffer.ResolveBreaks(); int defaultOffsetLength = defaultChunkId == -1 ? chunkBuffer.Size : chunkIdsToOffsets[defaultChunkId]; List <int> args = new List <int>() { defaultOffsetLength }; if (isInt) { foreach (int caseValue in integersToChunkIds.Keys.OrderBy(_ => _)) { int chunkId = integersToChunkIds[caseValue]; int offset = chunkIdsToOffsets[chunkId]; args.Add(caseValue); args.Add(offset); } } else { foreach (string caseValue in stringsToChunkIds.Keys.OrderBy(_ => _)) { int chunkId = stringsToChunkIds[caseValue]; int offset = chunkIdsToOffsets[chunkId]; args.Add(parser.GetStringConstant(caseValue)); args.Add(offset); } } buffer.Add( switchStatement.FirstToken, isInt ? OpCode.SWITCH_INT : OpCode.SWITCH_STRING, args.ToArray()); buffer.Concat(chunkBuffer); }
public static void Compile(ByteCodeCompiler bcc, ParserContext parser, ByteBuffer buffer, TryStatement tryStatement) { ByteBuffer tryCode = new ByteBuffer(); bcc.Compile(parser, tryCode, tryStatement.TryBlock); if (tryStatement.TryBlock.Length > 0 && tryStatement.TryBlock[0] is TryStatement) { // If the try block begins with another try block, that'll mess with the EsfToken metadata // which is declared at the beginning of the try block's PC and is singular. // Get around this limitation by tacking on a noop (JUMP +0) in this reasonably rare edge case. tryCode.AddFrontSlow(null, OpCode.JUMP, 0); } List <ByteBuffer> catchBlocks = new List <ByteBuffer>(); for (int i = 0; i < tryStatement.CatchBlocks.Length; ++i) { TryStatement.CatchBlock catchBlock = tryStatement.CatchBlocks[i]; ByteBuffer catchBlockBuffer = new ByteBuffer(); bcc.Compile(parser, catchBlockBuffer, catchBlock.Code); catchBlocks.Add(catchBlockBuffer); } ByteBuffer finallyCode = new ByteBuffer(); bcc.Compile(parser, finallyCode, tryStatement.FinallyBlock); finallyCode.ResolveBreaksAndContinuesForFinally(false); finallyCode.Add(null, OpCode.FINALLY_END, new int[] { // First 2 args are the same as a BREAK op code // Last 2 args are the same as a CONTINUE op code // These are all 0 and are resolved into their final values in the same pass as BREAK and CONTINUE 0, // break flag 0|1|2 0, // break offset 0, // continue flag 0|1|2 0 // continue offset }); // All user code is now compiled and offsets are sort of known. // Now build a lookup jump router thingy for all the catch blocks, if any. ByteBuffer allCatchBlocks = new ByteBuffer(); if (catchBlocks.Count > 0) { /* * It'll look something like this... * 0 EXCEPTION_HANDLED_TOGGLE true * 1 JUMP_IF_EXCEPTION_IS_TYPE offset varId type1, type2, ... * 2 JUMP_IF_EXCEPTION_IS_TYPE offset varId type3 * 3 EXCEPTION_HANDLED_TOGGLE false * 4 JUMP [to finally] * * 5 catch block 1 * ... * 22 last line in catch block 1 * 23 JUMP [to finally] * * 24 catch block 2... * ... * 72 last line in catch block 2 * * 73 finally code begins... */ // Add jumps to the end of each catch block to jump to the end. // Going in reverse order is easier for this. int totalSize = 0; for (int i = catchBlocks.Count - 1; i >= 0; --i) { ByteBuffer catchBlockBuffer = catchBlocks[i]; if (totalSize > 0) // omit the last block since a JUMP 0 is pointless. { catchBlockBuffer.Add(null, OpCode.JUMP, totalSize); } totalSize += catchBlockBuffer.Size; } // Now generate the header. This is also done backwards since it's easier. ByteBuffer exceptionSortHeader = new ByteBuffer(); int offset = 2 // EXCEPTION_HANDLED_TOGGLE + final JUMP + catchBlocks.Count - 1; // remaining jump instructions to jump over // Add all the JUMP_IF_EXCEPTION_OF_TYPE instructions. for (int i = 0; i < catchBlocks.Count; ++i) { TryStatement.CatchBlock cb = tryStatement.CatchBlocks[i]; ByteBuffer cbByteBuffer = catchBlocks[i]; int variableId = cb.VariableLocalScopeId.ID; // for each catch block insert a type-check-jump List <int> typeCheckArgs = new List <int>() { offset, variableId }; // first arg is offset, second is variable ID (or -1), successive args are all class ID's typeCheckArgs.AddRange(cb.TypeClasses.Select <ClassDefinition, int>(cd => cd.ClassID)); exceptionSortHeader.Add(null, OpCode.JUMP_IF_EXCEPTION_OF_TYPE, typeCheckArgs.ToArray()); // add the block to the running total offset += cbByteBuffer.Size; // ...but subtract 1 for the JUMP_IF_EXCEPTION_OF_TYPE you just added. offset -= 1; } exceptionSortHeader.Add(null, OpCode.EXCEPTION_HANDLED_TOGGLE, 0); exceptionSortHeader.Add(null, OpCode.JUMP, totalSize); allCatchBlocks.Add(null, OpCode.EXCEPTION_HANDLED_TOGGLE, 1); allCatchBlocks.Concat(exceptionSortHeader); foreach (ByteBuffer catchBlock in catchBlocks) { allCatchBlocks.Concat(catchBlock); } } int tryBegin = buffer.Size; buffer.Concat(tryCode); buffer.Add(null, OpCode.JUMP, allCatchBlocks.Size); buffer.Concat(allCatchBlocks); buffer.ResolveBreaksAndContinuesForFinally(true); buffer.Concat(finallyCode); int offsetToCatch = tryCode.Size + 1; int offsetToFinally = offsetToCatch + allCatchBlocks.Size; buffer.SetEsfToken(tryBegin, offsetToCatch, offsetToFinally); }
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); }