private static void _emitInstructions(CompilerState state) { //Tables of foreach call hint hooks var foreachCasts = new Dictionary<int, ForeachHint>(); var foreachGetCurrents = new Dictionary<int, ForeachHint>(); var foreachMoveNexts = new Dictionary<int, ForeachHint>(); var foreachDisposes = new Dictionary<int, ForeachHint>(); foreach (var hint in state._ForeachHints) { foreachCasts.Add(hint.CastAddress, hint); foreachGetCurrents.Add(hint.GetCurrentAddress, hint); foreachMoveNexts.Add(hint.MoveNextAddress, hint); foreachDisposes.Add(hint.DisposeAddress, hint); } var sourceCode = state.Source.Code; //CIL Extension var cilExtensionMode = false; List<CompileTimeValue> staticArgv = null; for (var instructionIndex = 0; instructionIndex < sourceCode.Count; instructionIndex++) { #region Handling for try-finally-catch blocks //Handle try-finally-catch blocks //Push new blocks foreach (var block in state.Seh.GetOpeningTryBlocks(instructionIndex)) { state.TryBlocks.Push(block); if (block.HasFinally) state.Il.BeginExceptionBlock(); if (block.HasCatch) state.Il.BeginExceptionBlock(); } //Handle active blocks if (state.TryBlocks.Count > 0) { CompiledTryCatchFinallyBlock block; do { block = state.TryBlocks.Peek(); if (instructionIndex == block.BeginFinally) { if (block.SkipTry == instructionIndex) { //state.Il.MarkLabel(block.SkipTryLabel); state.Il.Emit(OpCodes.Nop); } state.Il.BeginFinallyBlock(); } else if (instructionIndex == block.BeginCatch) { if (block.HasFinally) state.Il.EndExceptionBlock(); //end finally here state.Il.BeginCatchBlock(typeof (Exception)); //parse the exception state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, Runtime.ParseExceptionMethod, null); //user code will store it in a local variable } else if (instructionIndex == block.EndTry) { if (block.HasFinally || block.HasCatch) state.Il.EndExceptionBlock(); if (block.SkipTry == instructionIndex) { //state.Il.MarkLabel(block.SkipTryLabel); state.Il.Emit(OpCodes.Nop); } state.TryBlocks.Pop(); block = null; //signal another loop iteration } } while (block == null && state.TryBlocks.Count > 0); } #endregion state.MarkInstruction(instructionIndex); var ins = sourceCode[instructionIndex]; #region CIL hints // **** CIL hints **** // * CIL Extension * { if (state._CilExtensionOffsets.Count > 0 && state._CilExtensionOffsets.Peek() == instructionIndex) { state._CilExtensionOffsets.Dequeue(); if (staticArgv == null) staticArgv = new List<CompileTimeValue>(8); else staticArgv.Clear(); cilExtensionMode = true; } if (cilExtensionMode) { CompileTimeValue compileTimeValue; if (CompileTimeValue.TryParse(ins, state.IndexMap, state.Cache, state.Source.ParentApplication.Module.Name, out compileTimeValue)) { staticArgv.Add(compileTimeValue); } else { //found the actual invocation of the CIL extension cilExtensionMode = false; switch (ins.OpCode) { case OpCode.cmd: PCommand command; ICilExtension extension; if ( !state.TargetEngine.Commands.TryGetValue(ins.Id, out command) || (extension = command as ICilExtension) == null) goto default; extension.Implement(state, ins, staticArgv.ToArray(), ins.Arguments - staticArgv.Count); break; default: throw new PrexoniteException( "The CIL compiler does not support CIL extensions for this opcode: " + ins); } } continue; } } // * Foreach * { ForeachHint hint; if (foreachCasts.TryGetValue(instructionIndex, out hint)) { //result of (expr).GetEnumerator on the stack //cast IEnumerator state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, Runtime.ExtractEnumeratorMethod, null); instructionIndex++; //stloc enum state.EmitStoreLocal(state.Symbols[hint.EnumVar].Local); continue; } else if (foreachGetCurrents.TryGetValue(instructionIndex, out hint)) { //ldloc enum state.EmitLoadLocal(state.Symbols[hint.EnumVar].Local); instructionIndex++; //get.0 Current state.Il.EmitCall(OpCodes.Callvirt, ForeachHint.GetCurrentMethod, null); //result will be stored by user code continue; } else if (foreachMoveNexts.TryGetValue(instructionIndex, out hint)) { //ldloc enum state.EmitLoadLocal(state.Symbols[hint.EnumVar].Local); instructionIndex++; //get.0 MoveNext state.Il.EmitCall(OpCodes.Callvirt, ForeachHint.MoveNextMethod, null); instructionIndex++; //jump.t begin var target = sourceCode[instructionIndex].Arguments; //read from user code state.Il.Emit(OpCodes.Brtrue, state.InstructionLabels[target]); continue; } else if (foreachDisposes.TryGetValue(instructionIndex, out hint)) { //ldloc enum state.EmitLoadLocal(state.Symbols[hint.EnumVar].Local); instructionIndex++; //@cmd.1 dispose state.Il.EmitCall(OpCodes.Callvirt, ForeachHint.DisposeMethod, null); continue; } } #endregion // * Normal code generation * //Decode instruction var argc = ins.Arguments; var justEffect = ins.JustEffect; var id = ins.Id; int idx; string methodId; string typeExpr; var moduleName = ins.ModuleName; //Emit code for the instruction switch (ins.OpCode) { #region NOP //NOP case OpCode.nop: //Do nothing state.Il.Emit(OpCodes.Nop); break; #endregion #region LOAD #region LOAD CONSTANT //LOAD CONSTANT case OpCode.ldc_int: state.EmitLoadIntAsPValue(argc); break; case OpCode.ldc_real: state.EmitLoadRealAsPValue(ins); break; case OpCode.ldc_bool: state.EmitLoadBoolAsPValue(argc != 0); break; case OpCode.ldc_string: state.EmitLoadStringAsPValue(id); break; case OpCode.ldc_null: state.EmitLoadNullAsPValue(); break; #endregion LOAD CONSTANT #region LOAD REFERENCE //LOAD REFERENCE case OpCode.ldr_loc: state.EmitLoadLocalRefAsPValue(id); break; case OpCode.ldr_loci: id = state.IndexMap[argc]; goto case OpCode.ldr_loc; case OpCode.ldr_glob: state.EmitLoadGlobalRefAsPValue(id, moduleName); break; case OpCode.ldr_func: state.EmitLoadFuncRefAsPValue(id, moduleName); break; case OpCode.ldr_cmd: state.EmitLoadCmdRefAsPValue(id); break; case OpCode.ldr_app: CompilerState.EmitLoadAppRefAsPValue(state); break; case OpCode.ldr_eng: state.EmitLoadEngRefAsPValue(); break; case OpCode.ldr_type: state.EmitPTypeAsPValue(id); break; case OpCode.ldr_mod: state.EmitModuleNameAsPValue(moduleName); break; #endregion //LOAD REFERENCE #endregion //LOAD #region VARIABLES #region LOCAL //LOAD LOCAL VARIABLE case OpCode.ldloc: state.EmitLoadPValue(state.Symbols[id]); break; case OpCode.stloc: //Don't use EmitStorePValue here, because this is a more efficient solution var sym = state.Symbols[id]; if (sym.Kind == SymbolKind.Local) { state.EmitStoreLocal(sym.Local.LocalIndex); } else if (sym.Kind == SymbolKind.LocalRef) { state.EmitStoreLocal(state.PrimaryTempLocal.LocalIndex); state.EmitLoadLocal(sym.Local.LocalIndex); state.EmitLoadLocal(state.PrimaryTempLocal.LocalIndex); state.Il.EmitCall(OpCodes.Call, SetValueMethod, null); } break; case OpCode.ldloci: id = state.IndexMap[argc]; goto case OpCode.ldloc; case OpCode.stloci: id = state.IndexMap[argc]; goto case OpCode.stloc; #endregion #region GLOBAL //LOAD GLOBAL VARIABLE case OpCode.ldglob: state.EmitLoadGlobalValue(id, moduleName); break; case OpCode.stglob: state.EmitStoreLocal(state.PrimaryTempLocal); state.EmitLoadGlobalReference(id,moduleName); state.EmitLoadLocal(state.PrimaryTempLocal); state.Il.EmitCall(OpCodes.Call, SetValueMethod, null); break; #endregion #endregion #region CONSTRUCTION //CONSTRUCTION case OpCode.newobj: state.EmitNewObj(id, argc); break; case OpCode.newtype: state.FillArgv(argc); state.EmitLoadLocal(state.SctxLocal); state.ReadArgv(argc); state.Il.Emit(OpCodes.Ldstr, id); state.Il.EmitCall(OpCodes.Call, Runtime.NewTypeMethod, null); break; case OpCode.newclo: //Collect shared variables MetaEntry[] entries; var func = state.Source.ParentApplication.Functions[id]; if (func.Meta.ContainsKey(PFunction.SharedNamesKey)) entries = func.Meta[PFunction.SharedNamesKey].List; else entries = new MetaEntry[] {}; var hasSharedVariables = entries.Length > 0; if (hasSharedVariables) { state.EmitLdcI4(entries.Length); state.Il.Emit(OpCodes.Newarr, typeof (PVariable)); state.EmitStoreLocal(state.SharedLocal); for (var i = 0; i < entries.Length; i++) { state.EmitLoadLocal(state.SharedLocal); state.EmitLdcI4(i); state.EmitLoadLocal(state.Symbols[entries[i].Text].Local); state.Il.Emit(OpCodes.Stelem_Ref); } } state.EmitLoadLocal(state.SctxLocal); if (hasSharedVariables) state.EmitLoadLocal(state.SharedLocal); else state.Il.Emit(OpCodes.Ldnull); state.EmitNewClo(id,moduleName); break; case OpCode.newcor: state.FillArgv(argc); state.EmitLoadLocal(state.SctxLocal); state.ReadArgv(argc); state.Il.EmitCall(OpCodes.Call, Runtime.NewCoroutineMethod, null); break; #endregion #region OPERATORS #region UNARY //UNARY OPERATORS case OpCode.incloc: sym = state.Symbols[id]; if (sym.Kind == SymbolKind.Local) { state.EmitLoadLocal(sym.Local); state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, PVIncrementMethod, null); state.EmitStoreLocal(sym.Local); } else if (sym.Kind == SymbolKind.LocalRef) { state.EmitLoadLocal(sym.Local); state.Il.Emit(OpCodes.Dup); state.Il.EmitCall(OpCodes.Call, GetValueMethod, null); state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, PVIncrementMethod, null); state.Il.EmitCall(OpCodes.Call, SetValueMethod, null); } break; case OpCode.incloci: id = state.IndexMap[argc]; goto case OpCode.incloc; case OpCode.incglob: state.EmitLoadGlobalReference(id,moduleName); state.Il.Emit(OpCodes.Dup); state.Il.EmitCall(OpCodes.Call, GetValueMethod, null); state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, PVIncrementMethod, null); state.Il.EmitCall(OpCodes.Call, SetValueMethod, null); break; case OpCode.decloc: sym = state.Symbols[id]; if (sym.Kind == SymbolKind.Local) { state.EmitLoadLocal(sym.Local); state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, PVDecrementMethod, null); state.EmitStoreLocal(sym.Local); } else if (sym.Kind == SymbolKind.LocalRef) { state.EmitLoadLocal(sym.Local); state.Il.Emit(OpCodes.Dup); state.Il.EmitCall(OpCodes.Call, GetValueMethod, null); state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, PVDecrementMethod, null); state.Il.EmitCall(OpCodes.Call, SetValueMethod, null); } break; case OpCode.decloci: id = state.IndexMap[argc]; goto case OpCode.decloc; case OpCode.decglob: state.EmitLoadGlobalReference(id,moduleName); state.Il.Emit(OpCodes.Dup); state.Il.EmitCall(OpCodes.Call, GetValueMethod, null); state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, PVDecrementMethod, null); state.Il.EmitCall(OpCodes.Call, SetValueMethod, null); break; #endregion #region BINARY // all binary operators are implemented as CIL extensions in // Prexonite.Commands.Core.Operators #endregion //OPERATORS #endregion #region TYPE OPERATIONS #region TYPE CHECK //TYPE CHECK case OpCode.check_const: //Stack: // Obj state.EmitLoadType(id); //Stack: // Obj // Type state.EmitCall(Runtime.CheckTypeConstMethod); break; case OpCode.check_arg: //Stack: // Obj // Type state.Il.EmitCall(OpCodes.Call, Runtime.CheckTypeMethod, null); break; case OpCode.check_null: state.Il.EmitCall(OpCodes.Call, PVIsNullMethod, null); state.Il.Emit(OpCodes.Box, typeof (bool)); state.Il.EmitCall(OpCodes.Call, GetBoolPType, null); state.Il.Emit(OpCodes.Newobj, NewPValue); break; #endregion #region TYPE CAST case OpCode.cast_const: //Stack: // Obj state.EmitLoadType(id); //Stack: // Obj // Type state.EmitLoadLocal(state.SctxLocal); state.EmitCall(Runtime.CastConstMethod); break; case OpCode.cast_arg: //Stack // Obj // Type state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, Runtime.CastMethod, null); break; #endregion #endregion #region OBJECT CALLS #region DYNAMIC case OpCode.get: state.FillArgv(argc); state.EmitLoadLocal(state.SctxLocal); state.ReadArgv(argc); state.EmitLdcI4((int) PCall.Get); state.Il.Emit(OpCodes.Ldstr, id); state.Il.EmitCall(OpCodes.Call, PVDynamicCallMethod, null); if (justEffect) state.Il.Emit(OpCodes.Pop); break; case OpCode.set: state.FillArgv(argc); state.EmitLoadLocal(state.SctxLocal); state.ReadArgv(argc); state.EmitLdcI4((int) PCall.Set); state.Il.Emit(OpCodes.Ldstr, id); state.Il.EmitCall(OpCodes.Call, PVDynamicCallMethod, null); state.Il.Emit(OpCodes.Pop); break; #endregion #region STATIC case OpCode.sget: //Stack: // arg // . // . // . state.FillArgv(argc); idx = id.LastIndexOf("::"); if (idx < 0) throw new PrexoniteException ( "Invalid sget instruction. Does not specify a method."); methodId = id.Substring(idx + 2); typeExpr = id.Substring(0, idx); state.EmitLoadType(typeExpr); state.EmitLoadLocal(state.SctxLocal); state.ReadArgv(argc); state.EmitLdcI4((int) PCall.Get); state.Il.Emit(OpCodes.Ldstr, methodId); state.EmitVirtualCall(Runtime.StaticCallMethod); if (justEffect) state.Il.Emit(OpCodes.Pop); break; case OpCode.sset: state.FillArgv(argc); idx = id.LastIndexOf("::"); if (idx < 0) throw new PrexoniteException ( "Invalid sset instruction. Does not specify a method."); methodId = id.Substring(idx + 2); typeExpr = id.Substring(0, idx); state.EmitLoadType(typeExpr); state.EmitLoadLocal(state.SctxLocal); state.ReadArgv(argc); state.EmitLdcI4((int) PCall.Set); state.Il.Emit(OpCodes.Ldstr, methodId); state.EmitVirtualCall(Runtime.StaticCallMethod); state.Il.Emit(OpCodes.Pop); break; #endregion #endregion #region INDIRECT CALLS case OpCode.indloc: sym = state.Symbols[id]; if (sym == null) throw new PrexoniteException( "Internal CIL compiler error. Information about local entity " + id + " missing."); state.FillArgv(argc); sym.EmitLoad(state); state.EmitIndirectCall(argc, justEffect); break; case OpCode.indloci: idx = argc & ushort.MaxValue; argc = (argc & (ushort.MaxValue << 16)) >> 16; id = state.IndexMap[idx]; goto case OpCode.indloc; case OpCode.indglob: state.FillArgv(argc); state.EmitLoadGlobalValue(id,moduleName); state.EmitIndirectCall(argc, justEffect); break; case OpCode.indarg: //Stack // obj // args state.FillArgv(argc); state.EmitIndirectCall(argc, justEffect); break; case OpCode.tail: throw new PrexoniteException( "Cannot compile tail instruction to CIL. Qualification should have failed."); #endregion #region ENGINE CALLS case OpCode.func: state.EmitFuncCall(argc, id, moduleName, justEffect); break; case OpCode.cmd: state.EmitCommandCall(ins); break; #endregion #region FLOW CONTROL //FLOW CONTROL #region JUMPS case OpCode.jump: case OpCode.jump_t: case OpCode.jump_f: case OpCode.ret_break: case OpCode.ret_continue: state.Seh.EmitJump(instructionIndex, ins); break; #endregion #region RETURNS case OpCode.ret_exit: goto case OpCode.jump; case OpCode.ret_value: //return value is assigned by SEH goto case OpCode.jump; case OpCode.ret_set: state.EmitSetReturnValue(); break; #endregion #region THROW case OpCode.@throw: state.EmitLoadLocal(state.SctxLocal); state.Il.EmitCall(OpCodes.Call, Runtime.ThrowExceptionMethod, null); break; #endregion #region LEAVE case OpCode.@try: //Is done via analysis of TryCatchFinally objects associated with the function Debug.Assert(state.StackSize[instructionIndex] == 0, "The stack should be empty when entering a try-block.", "The stack is not empty when entering the try-block at instruction {0} in function {1}.", instructionIndex, state.Source); break; case OpCode.leave: //is handled by the CLR #endregion #region EXCEPTION case OpCode.exc: //is not implemented via Emit // The exception is stored when the exception block is entered. break; #endregion #endregion #region STACK MANIPULATION //STACK MANIPULATION case OpCode.pop: for (var i = 0; i < argc; i++) state.Il.Emit(OpCodes.Pop); break; case OpCode.dup: for (var i = 0; i < argc; i++) state.Il.Emit(OpCodes.Dup); break; case OpCode.rot: var values = (int) ins.GenericArgument; var rotations = argc; for (var i = 0; i < values; i++) state.EmitStoreLocal ( state.TempLocals[(i + rotations)%values].LocalIndex); for (var i = values - 1; i >= 0; i--) state.EmitLoadLocal(state.TempLocals[i].LocalIndex); break; #endregion } //end of switch over opcode //DON'T ADD ANY CODE HERE, A LOT OF CASES USE `CONTINUE` } // end of loop over instructions //Close all pending try blocks, since the next instruction will never come // (other closing try blocks are handled by the emitting the instruction immediately following // the try block) foreach (var block in state.TryBlocks) { if (block.HasCatch || block.HasFinally) state.Il.EndExceptionBlock(); } //Implicit return //Often instructions refer to a virtual instruction after the last real one. state.MarkInstruction(sourceCode.Count); state.Il.MarkLabel(state.ReturnLabel); state.Il.Emit(OpCodes.Ret); }