protected override void DoEmitCode(CompilerTarget target, StackSemantics stackSemantics) { if(stackSemantics == StackSemantics.Value) throw new NotSupportedException("Foreach loops don't produce values and can thus not be emitted with value semantics."); if (!IsInitialized) throw new PrexoniteException("AstForeachLoop requires List and Element to be set."); //Optimize expression _OptimizeNode(target, ref List); //Create the enumerator variable var enumVar = Block.CreateLabel("enumerator"); target.Function.Variables.Add(enumVar); //Create the element assignment statement var element = Element.GetCopy(); AstExpr optElem; if (element.TryOptimize(target, out optElem)) { element = optElem as AstGetSet; if (element == null) { target.Loader.ReportMessage(Message.Error(Resources.AstForeachLoop_DoEmitCode_ElementTooComplicated,Position,MessageClasses.ForeachElementTooComplicated)); return; } } var ldEnumVar = target.Factory.Call(Position, EntityRef.Variable.Local.Create(enumVar)); var getCurrent = new AstGetSetMemberAccess(File, Line, Column, ldEnumVar, "Current"); element.Arguments.Add(getCurrent); element.Call = PCall.Set; //Actual Code Generation var moveNextAddr = -1; var getCurrentAddr = -1; var disposeAddr = -1; //Get the enumerator target.BeginBlock(Block); List.EmitValueCode(target); target.EmitGetCall(List.Position, 0, "GetEnumerator"); var castAddr = target.Code.Count; target.Emit(List.Position, OpCode.cast_const, "Object(\"System.Collections.IEnumerator\")"); target.EmitStoreLocal(List.Position, enumVar); //check whether an enhanced CIL implementation is possible bool emitHint; if (element.DefaultAdditionalArguments + element.Arguments.Count > 1) //has additional arguments emitHint = false; else emitHint = true; var @try = new AstTryCatchFinally(Position, Block); @try.TryBlock = new AstActionBlock ( Position, @try, delegate { target.EmitJump(Position, Block.ContinueLabel); //Assignment (begin) target.EmitLabel(Position, Block.BeginLabel); getCurrentAddr = target.Code.Count; element.EmitEffectCode(target); //Code block Block.EmitEffectCode(target); //Condition (continue) target.EmitLabel(Position, Block.ContinueLabel); moveNextAddr = target.Code.Count; target.EmitLoadLocal(List.Position, enumVar); target.EmitGetCall(List.Position, 0, "MoveNext"); target.EmitJumpIfTrue(Position, Block.BeginLabel); //Break target.EmitLabel(Position, Block.BreakLabel); }); @try.FinallyBlock = new AstActionBlock ( Position, @try, delegate { disposeAddr = target.Code.Count; target.EmitLoadLocal(List.Position, enumVar); target.EmitCommandCall(List.Position, 1, Engine.DisposeAlias, true); }); @try.EmitEffectCode(target); target.EndBlock(); if (getCurrentAddr < 0 || moveNextAddr < 0 || disposeAddr < 0) throw new PrexoniteException( "Could not capture addresses within foreach construct for CIL compiler hint."); else if (emitHint) { var hint = new ForeachHint(enumVar, castAddr, getCurrentAddr, moveNextAddr, disposeAddr); Cil.Compiler.AddCilHint(target, hint); Action<int, int> mkHook = (index, original) => { AddressChangeHook hook = null; hook = new AddressChangeHook( original, newAddr => { foreach ( var hintEntry in target.Meta[Loader.CilHintsKey].List) { var entry = hintEntry.List; if (entry[0] == ForeachHint.Key && entry[index].Text == original.ToString(CultureInfo.InvariantCulture)) { entry[index] = newAddr.ToString(CultureInfo.InvariantCulture); // AddressChangeHook.ctor can be trusted not to call the closure. // ReSharper disable PossibleNullReferenceException // ReSharper disable AccessToModifiedClosure hook.InstructionIndex = newAddr; // ReSharper restore AccessToModifiedClosure // ReSharper restore PossibleNullReferenceException original = newAddr; } } }); target.AddressChangeHooks.Add(hook); }; mkHook(ForeachHint.CastAddressIndex + 1, castAddr); mkHook(ForeachHint.GetCurrentAddressIndex + 1, getCurrentAddr); mkHook(ForeachHint.MoveNextAddressIndex + 1, moveNextAddr); mkHook(ForeachHint.DisposeAddressIndex + 1, disposeAddr); } // else nothing }
protected override void DoEmitCode(CompilerTarget target, StackSemantics stackSemantics) { if(stackSemantics == StackSemantics.Value) throw new NotSupportedException("While loops do not produce values and can thus not be used as expressions."); if (!IsInitialized) throw new PrexoniteException("AstWhileLoop requires Condition to be set."); //Optimize unary not condition _OptimizeNode(target, ref Condition); // Invert condition when unary logical not AstIndirectCall unaryCond; while (Condition.IsCommandCall(Commands.Core.Operators.LogicalNot.DefaultAlias, out unaryCond)) { Condition = unaryCond.Arguments[0]; IsPositive = !IsPositive; } //Constant conditions var conditionIsConstant = false; if (Condition is AstConstant) { var constCond = (AstConstant) Condition; PValue condValue; if ( !constCond.ToPValue(target).TryConvertTo( target.Loader, PType.Bool, out condValue)) goto continueFull; else if ((bool) condValue.Value == IsPositive) conditionIsConstant = true; else { //Condition is always false if (!IsPrecondition) //If do-while, emit the body without loop code { target.BeginBlock(Block); Block.EmitEffectCode(target); target.EndBlock(); } return; } } continueFull: target.BeginBlock(Block); if (!Block.IsEmpty) //Body exists -> complete loop code? { if (conditionIsConstant) //Infinite, hopefully user managed, loop -> { target.EmitLabel(Position, Block.ContinueLabel); target.EmitLabel(Position, Block.BeginLabel); Block.EmitEffectCode(target); target.EmitJump(Position, Block.ContinueLabel); } else { if (IsPrecondition) target.EmitJump(Position, Block.ContinueLabel); target.EmitLabel(Position, Block.BeginLabel); Block.EmitEffectCode(target); _emitCondition(target); } } else //Body does not exist -> Condition loop { target.EmitLabel(Position, Block.BeginLabel); _emitCondition(target); } target.EmitLabel(Position, Block.BreakLabel); target.EndBlock(); }
protected override void DoEmitCode(CompilerTarget target, StackSemantics stackSemantics) { if(stackSemantics == StackSemantics.Value) throw new NotSupportedException("For loops don't produce values and can thus not be emitted with value semantics."); if (!IsInitialized) throw new PrexoniteException("AstForLoop requires Condition to be set."); //Optimize unary not condition var condition = Condition; _OptimizeNode(target, ref condition); // Invert condition when unary logical not AstIndirectCall unaryCond; while (Condition.IsCommandCall(Commands.Core.Operators.LogicalNot.DefaultAlias, out unaryCond)) { Condition = unaryCond.Arguments[0]; IsPositive = !IsPositive; } //Constant conditions var conditionIsConstant = false; var constCond = condition as AstConstant; if (constCond != null) { PValue condValue; if ( !constCond.ToPValue(target).TryConvertTo( target.Loader, PType.Bool, out condValue)) goto continueFull; else if ((bool) condValue.Value == IsPositive) conditionIsConstant = true; else { //Condition is always false return; } } continueFull: var conditionLabel = Block.CreateLabel("condition"); if (!Block.IsEmpty) //Body exists -> complete loop code? { if (conditionIsConstant) //Infinite, hopefully user managed, loop -> { /* {init} * begin: * {block} * continue: * {next} * jump -> begin */ target.BeginBlock(Initialize); Initialize.EmitValueCode(target); if (!IsPrecondition) //start with nextIteration target.EmitJump(Position, Block.ContinueLabel); target.EmitLabel(Position, Block.BeginLabel); target.BeginBlock(NextIteration); target.BeginBlock(Block); Block.EmitEffectCode(target); target.EndBlock(); target.EmitLabel(Position, Block.ContinueLabel); NextIteration.EmitValueCode(target); target.EndBlock(); target.EmitJump(Position, Block.BeginLabel); target.EndBlock(); } else //Variable condition and body -> full loop code { /* {init} * jump -> condition * begin: * {block} * continue: * {next} * condition: * {condition} * jump if true -> begin */ target.BeginBlock(Initialize); Initialize.EmitValueCode(target); target.BeginBlock(NextIteration); if (IsPrecondition) target.EmitJump(Position, conditionLabel); else target.EmitJump(Position, Block.ContinueLabel); target.EmitLabel(Position, Block.BeginLabel); target.BeginBlock(Block); Block.EmitEffectCode(target); target.EndBlock(); target.EmitLabel(Position, Block.ContinueLabel); NextIteration.EmitValueCode(target); target.EndBlock(); target.EmitLabel(Position, conditionLabel); AstLazyLogical.EmitJumpCondition( target, condition, Block.BeginLabel, IsPositive); target.EndBlock(); } } else //Body does not exist -> Condition loop { /* {init} * begin: * {cond} * jump if false -> break * continue: * {next} * jump -> begin */ target.BeginBlock(Block); Initialize.EmitValueCode(target); if (!IsPrecondition) target.EmitJump(Position, Block.ContinueLabel); target.EmitLabel(Position, Block.BeginLabel); AstLazyLogical.EmitJumpCondition(target, condition, Block.BreakLabel, !IsPositive); if (IsPrecondition) target.EmitLabel(Position, Block.ContinueLabel); NextIteration.EmitValueCode(target); target.EmitJump(Position, Block.BeginLabel); target.EndBlock(); } target.EmitLabel(Position, Block.BreakLabel); }