protected override ICode VisitIf(StmtIf s)
        {
            if (!VisitorFindContinuations.Any(s))
            {
                // 'If' contains no continuations, so no distribution can be done
                return(s);
            }
            if (VisitorOnlyStatements.Only(s, Stmt.NodeType.If, Stmt.NodeType.Continuation) && this.ifInfo == null)
            {
                // 'If' only contains continuations, so no distribution can be done
                // Must visit base method to find contained continuations
                return(base.VisitIf(s));
            }
            bool finalise = false;

            if (this.ifInfo == null)
            {
                finalise    = true;
                this.ifInfo = new IfInfo();
            }
            this.ifInfo.Conditions.Push(s.Condition);
            var then = this.Visit(s.Then);

            this.ifInfo.Conditions.Pop();
            this.ifInfo.Conditions.Push(this.ctx.ExprGen.NotAutoSimplify(s.Condition));
            var @else = this.Visit(s.Else);

            this.ifInfo.Conditions.Pop();
            if (then != s.Then || @else != s.Else)
            {
                var @if = new StmtIf(s.Ctx, s.Condition, (Stmt)then, (Stmt)@else);
                if (finalise && this.ifInfo.AddToIf.Any())
                {
                    var ifStmts = this.ifInfo.AddToIf.GroupBy(x => x.Item1.To, x => x.Item2).Select(x =>
                                                                                                    new StmtIf(s.Ctx, x.Aggregate((a, b) => this.ctx.ExprGen.Or(a, b)),
                                                                                                               this.ifInfo.AddToIf.First(y => y.Item1.To == x.Key).Item1, null)
                                                                                                    );
                    var stmts = new Stmt[] { @if }.Concat(ifStmts).ToArray();
                    this.ifInfo = null;
                    return(new StmtBlock(s.Ctx, stmts));
                }
                else
                {
                    if (finalise)
                    {
                        this.ifInfo = null;
                    }
                    return(@if);
                }
            }
            else
            {
                // In this case, no continuations will have been found, so there cannot be any conditions to add
                if (finalise)
                {
                    this.ifInfo = null;
                }
                return(s);
            }
        }
Пример #2
0
        protected override ICode VisitDoLoop(StmtDoLoop s)
        {
            var                body   = (Stmt)this.Visit(s.Body);
            StmtIf             lastIf = null;
            IEnumerable <Stmt> preIf  = null;

            if (body.StmtType == Stmt.NodeType.Block)
            {
                var sBlock = (StmtBlock)body;
                if (sBlock.Statements.Any())
                {
                    var sLast = sBlock.Statements.Last();
                    if (sLast.StmtType == Stmt.NodeType.If)
                    {
                        lastIf = (StmtIf)sLast;
                        preIf  = sBlock.Statements.Take(sBlock.Statements.Count() - 1).ToArray();
                    }
                }
            }
            else if (body.StmtType == Stmt.NodeType.If)
            {
                lastIf = (StmtIf)body;
                preIf  = Enumerable.Empty <Stmt>();
            }
            if (lastIf != null)
            {
                Stmt   afterLoop = null;
                StmtIf newIf     = null;
                // See if final 'if' condition is same as the 'do' condition.
                // TODO: This may lead to a non-terminating situation...
                if (lastIf.Condition.DoesEqual(s.While))
                {
                    afterLoop = lastIf.Else;
                    newIf     = new StmtIf(s.Ctx, lastIf.Condition, lastIf.Then, null);
                }
                else if (lastIf.Condition.DoesEqualNot(s.While))
                {
                    afterLoop = lastIf.Then;
                    newIf     = new StmtIf(s.Ctx, lastIf.Condition, null, lastIf.Else);
                }
                if (afterLoop != null)
                {
                    var loopBody = new StmtBlock(s.Ctx, preIf.Concat(newIf));
                    var loop     = new StmtDoLoop(s.Ctx, loopBody, s.While);
                    var ret      = new StmtBlock(s.Ctx, loop, afterLoop);
                    return(ret);
                }
            }
            if (body != s.Body)
            {
                return(new StmtDoLoop(s.Ctx, body, s.While));
            }
            else
            {
                return(s);
            }
        }
Пример #3
0
        public static Stmt get_Value(Ctx ctx)
        {
            var excCtor = ctx.Module.Import(typeof(InvalidOperationException).GetConstructor(Type.EmptyTypes));
            var invalidOperationException      = new ExprNewObj(ctx, excCtor);
            var throwInvalidOperationException = new StmtThrow(ctx, invalidOperationException);
            var cond    = new ExprJsExplicit(ctx, "(this === null)", ctx.Boolean, ctx.ThisNamed);
            var throwIf = new StmtIf(ctx, cond, throwInvalidOperationException, null);
            var ret     = new StmtReturn(ctx, ctx.This);
            var stmt    = new StmtBlock(ctx, throwIf, ret);

            return(stmt);
        }
Пример #4
0
        internal override void EmitStmt(ILGenerator ilg)
        {
            Cond.Emit(ilg, true);
            var lb = ilg.DefineLabel();
            var le = ilg.DefineLabel();

            ilg.Emit(OpCodes.Brtrue, lb);
            StmtElse?.Emit(ilg);
            ilg.Emit(OpCodes.Br, le);
            ilg.MarkLabel(lb);
            StmtIf?.Emit(ilg);
            ilg.MarkLabel(le);
        }
Пример #5
0
        protected override ICode VisitIf(StmtIf s)
        {
            var ctx       = s.Ctx;
            var condition = (Expr)this.Visit(s.Condition);

            this.stack.Push(new List <ExprVar>());
            var then   = (Stmt)this.Visit(s.Then);
            var thenDA = this.stack.Pop();

            this.stack.Push(new List <ExprVar>());
            var @else  = (Stmt)this.Visit(s.Else);
            var elseDA = this.stack.Pop();

            var intersection = thenDA.Intersect(elseDA, (IEqualityComparer <ExprVar>) this.phiComparer).ToArray();

            this.stack.Peek().AddRange(intersection);

            var conditionVars = VisitorFindVars.V(condition);
            var needAssigning = conditionVars.Except(this.stack.SelectMany(x => x), (IEqualityComparer <ExprVar>) this.phiComparer).ToArray();

            if (needAssigning.Any())
            {
                var replacements = needAssigning.Select(x => {
                    var newExpr = ctx.Local(x.Type);
                    var phi     = new ExprVarPhi(ctx)
                    {
                        Exprs = new[] { x, newExpr }
                    };
                    return(new { orgExpr = x, newExpr, phi });
                }).ToArray();
                foreach (var replace in replacements)
                {
                    this.stack.Peek().Add(replace.newExpr);
                    this.stack.Peek().Add(replace.phi);
                    condition = (Expr)VisitorReplace.V(condition, replace.orgExpr, replace.phi);
                }
                this.ensureAssigned.Add(s, replacements.Select(x => x.newExpr).ToArray());
            }

            if (condition != s.Condition || then != s.Then || @else != s.Else)
            {
                var newS = new StmtIf(ctx, condition, then, @else);
                this.stmtMap.Add(newS, s);
                return(newS);
            }
            else
            {
                return(s);
            }
        }
Пример #6
0
        protected virtual ICode VisitIf(StmtIf s)
        {
            this.ThrowOnNoOverride();
            var condition = this.Visit(s.Condition);
            var then      = this.Visit(s.Then);
            var @else     = this.Visit(s.Else);

            if (condition != s.Condition || then != s.Then || @else != s.Else)
            {
                return(new StmtIf(s.Ctx, (Expr)condition, (Stmt)then, (Stmt)@else));
            }
            else
            {
                return(s);
            }
        }
Пример #7
0
 protected override ICode VisitIf(StmtIf s)
 {
     this.NewLine();
     this.js.Append("if (");
     this.Visit(s.Condition);
     this.js.Append(") {");
     this.indent++;
     this.Visit(s.Then);
     this.indent--;
     if (s.Else != null)
     {
         this.NewLine();
         this.js.Append("} else {");
         this.indent++;
         this.Visit(s.Else);
         this.indent--;
     }
     this.NewLine();
     this.js.Append("}");
     return(s);
 }
Пример #8
0
        private Stmt ParseIf()
        {
            StmtIf ifStmt = new StmtIf();

            ifStmt.IfToken = tape.Current;

            StmtIf.StmtIfTrue ifPart = ParseTruePart();
            ifStmt.Parts.Add(ifPart);
            while (tape.CurrentKind == TokenKind.ELSEIF)
            {
                StmtIf.StmtIfTrue elseifPart = ParseTruePart();
                ifStmt.Parts.Add(elseifPart);
            }
            if (tape.CurrentKind == TokenKind.ELSE)
            {
                CodePosition pos = tape.Current.Position;
                tape.MoveNext();
                ifStmt.ElsePart = ParseStmtBlock(pos);
            }
            return(ifStmt);
        }
        protected override ICode VisitBlock(StmtBlock s)
        {
            var ctx        = s.Ctx;
            var statements = s.Statements
                             .Select(x => (Stmt)this.Visit(x))
                             .Where(x => x != null)
                             .ToArray();
            var stNew = statements.Combine((a, b) => {
                // If two 'if' statements are both continuations, then bring a recursive continuation forwards if possible
                if (a.StmtType == Stmt.NodeType.If && b.StmtType == Stmt.NodeType.If)
                {
                    var aIf = (StmtIf)a;
                    var bIf = (StmtIf)b;
                    if (aIf.Then != null && bIf.Then != null &&
                        aIf.Then.StmtType == Stmt.NodeType.Continuation && bIf.Then.StmtType == Stmt.NodeType.Continuation &&
                        aIf.Else == null && bIf.Else == null)
                    {
                        var aCont = (StmtContinuation)aIf.Then;
                        var bCont = (StmtContinuation)bIf.Then;
                        if (aCont.To != this.block && bCont.To == this.block)
                        {
                            return(new StmtBlock(ctx, b, a));
                        }
                    }
                }
                return(null);
            })
                        .ToArray();

            if (!statements.SequenceEqual(stNew))
            {
                return(new StmtBlock(ctx, stNew));
            }
            // If an 'if' statement containing only a continuation is followed by any other kind of statement then swap them
            // (with suitable 'if' guard). Look ahead and encase as much as possible in the 'if' guard
            for (int i = 0; i < statements.Length - 1; i++)
            {
                var stmt = statements[i];
                if (stmt.StmtType == Stmt.NodeType.If)
                {
                    var sIf = (StmtIf)stmt;
                    if (sIf.Then != null && sIf.Then.StmtType == Stmt.NodeType.Continuation && sIf.Else == null)
                    {
                        var moveCount = 0;
                        for (int j = i + 1; j < statements.Length; j++)
                        {
                            var  b = statements[j];
                            bool move;
                            if (b.StmtType == Stmt.NodeType.Continuation)
                            {
                                move = false;
                            }
                            else if (b.StmtType != Stmt.NodeType.If)
                            {
                                move = true;
                            }
                            else
                            {
                                var bIf = (StmtIf)b;
                                move = bIf.Then == null || bIf.Then.StmtType != Stmt.NodeType.Continuation || bIf.Else != null;
                            }
                            if (move)
                            {
                                moveCount++;
                            }
                            else
                            {
                                break;
                            }
                        }
                        if (moveCount > 0)
                        {
                            var moveBlock = new StmtBlock(ctx, statements.Skip(i + 1).Take(moveCount));
                            var ifBlock   = new StmtIf(ctx, ctx.ExprGen.Not(sIf.Condition), moveBlock, null);
                            var allStmts  =
                                statements.Take(i)
                                .Concat(ifBlock)
                                .Concat(statements[i])
                                .Concat(statements.Skip(i + 1 + moveCount))
                                .ToArray();
                            return(new StmtBlock(ctx, allStmts));
                        }
                    }
                }
            }

            // Reorder if/continuations at end of block to group by continuation target
            var finalContsRev = statements.Reverse().TakeWhile(x => {
                if (x.StmtType == Stmt.NodeType.Continuation)
                {
                    return(true);
                }
                if (x.StmtType == Stmt.NodeType.If)
                {
                    var xIf = (StmtIf)x;
                    if (xIf.Else == null && xIf.Then.StmtType == Stmt.NodeType.Continuation)
                    {
                        return(true);
                    }
                }
                return(false);
            })
                                .Select(x => {
                Stmt to;
                if (x.StmtType == Stmt.NodeType.Continuation)
                {
                    to = ((StmtContinuation)x).To;
                }
                else
                {
                    to = ((StmtContinuation)((StmtIf)x).Then).To;
                }
                return(new { stmt = x, to });
            }).ToArray();
            var lookForMidCont = finalContsRev.Reverse().TakeWhile(x => x.stmt.StmtType != Stmt.NodeType.Continuation).ToArray();

            if (lookForMidCont.Length < finalContsRev.Length - 1)
            {
                // Look for non-if continuation in middle of final continuations
                // I suspect that this cannot ever occur
                var stmts = statements.Take(statements.Length - finalContsRev.Length)
                            .Concat(lookForMidCont.Select(x => x.stmt))
                            .Concat(finalContsRev.Select(x => x.stmt).Reverse().ElementAt(lookForMidCont.Length)).ToArray();
                return(new StmtBlock(s.Ctx, stmts));
            }
            if (finalContsRev.Count() >= 2)
            {
                var newFinal = finalContsRev.Combine((a, b) => {
                    if (a.stmt.StmtType == Stmt.NodeType.Continuation && a.to == b.to)
                    {
                        return(a);
                    }
                    return(null);
                });
                if (!finalContsRev.SequenceEqual(newFinal))
                {
                    var stmts = statements.Take(statements.Length - finalContsRev.Length).Concat(newFinal.Reverse().Select(x => x.stmt));
                    return(new StmtBlock(s.Ctx, stmts));
                }
                var dups = finalContsRev.GroupBy(x => x.to).Select(x => new { to = x.Key, count = x.Count() }).Where(x => x.count >= 2).ToArray();
                if (dups.Any())
                {
                    var finalStmts = finalContsRev.Reverse().ToArray();
                    foreach (var dup in dups)
                    {
                        var movingBack = new List <Stmt>();
                        var addIfs     = new Dictionary <Stmt, IEnumerable <Expr> >();
                        foreach (var stmt in finalStmts)
                        {
                            if (stmt.to == dup.to)
                            {
                                movingBack.Add(stmt.stmt);
                            }
                            else
                            {
                                addIfs.Add(stmt.stmt, movingBack.Select(x => s.Ctx.ExprGen.Not(((StmtIf)x).Condition)).ToArray());
                            }
                            if (movingBack.Count == dup.count)
                            {
                                finalStmts = finalStmts.SelectMany(x => {
                                    if (movingBack.Contains(x.stmt))
                                    {
                                        if (x.stmt == movingBack.Last())
                                        {
                                            return(movingBack.Select(y => new { stmt = (Stmt)y, to = x.to }));
                                        }
                                        else
                                        {
                                            return(finalStmts.EmptyOf());
                                        }
                                    }
                                    else
                                    {
                                        var addIf = addIfs.ValueOrDefault(x.stmt);
                                        if (addIf != null && addIf.Any())
                                        {
                                            var @if   = addIf.Aggregate((a, b) => s.Ctx.ExprGen.And(a, b));
                                            var newIf = new StmtIf(s.Ctx, @if, x.stmt, null);
                                            return(new[] { new { stmt = (Stmt)newIf, to = x.to } });
                                        }
                                        else
                                        {
                                            return(new[] { x });
                                        }
                                    }
                                }).ToArray();

                                break;
                            }
                        }
                    }
                    var stmts = statements.Take(statements.Length - finalContsRev.Length).Concat(finalStmts.Select(x => x.stmt));
                    return(new StmtBlock(s.Ctx, stmts));
                }
            }
            if (!statements.SequenceEqual(s.Statements))
            {
                return(new StmtBlock(ctx, statements));
            }
            else
            {
                return(s);
            }
        }
Пример #10
0
        private void BuildBlock(IEnumerable <Instruction> insts, Instruction startOfNextPart)
        {
            var inst0 = insts.First();
            var instN = insts.Last();
            // Any instruction in the method could target this block
            var blockStarts = this.methodBlockStarts
                              .Where(x => x.Offset >= inst0.Offset && x.Offset <= instN.Offset)
                              .ToArray();

            // For each block create a StmtCil, with the correct ending.
            // Endings that require a expression have them set to null; they will be filled in during CIL decoding
            for (int i = 0; i < blockStarts.Length; i++)
            {
                var  start      = blockStarts[i];
                var  end        = i == blockStarts.Length - 1 ? insts.Last() : blockStarts[i + 1].Previous;
                var  blockInsts = start.GetRange(end);
                Stmt blockEndStmt;
                var  code = end.OpCode.Code;
                switch (end.OpCode.FlowControl)
                {
                case FlowControl.Cond_Branch:
                    if (code == Code.Switch)
                    {
                        var cases = ((Instruction[])end.Operand).Select(x => new StmtContinuation(this.ctx, x, false)).ToArray();
                        foreach (var @case in cases)
                        {
                            this.mappable.Add(@case);
                        }
                        var @default = new StmtContinuation(this.ctx, end.Next, false);
                        this.mappable.Add(@default);
                        blockEndStmt = new StmtSwitch(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Int32),
                                                      cases.Select((x, value) => new StmtSwitch.Case(value, x)).ToArray(),
                                                      @default);
                    }
                    else
                    {
                        var ifTrue  = new StmtContinuation(this.ctx, (Instruction)end.Operand, false);
                        var ifFalse = new StmtContinuation(this.ctx, end.Next, false);
                        this.mappable.Add(ifTrue);
                        this.mappable.Add(ifFalse);
                        blockEndStmt = new StmtIf(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Boolean), ifTrue, ifFalse);
                    }
                    break;

                case FlowControl.Branch:
                    var leaveProtectedRegion = code == Code.Leave || code == Code.Leave_S;
                    blockEndStmt = new StmtContinuation(this.ctx, (Instruction)end.Operand, leaveProtectedRegion);
                    this.mappable.Add((StmtContinuation)blockEndStmt);
                    break;

                case FlowControl.Next:
                case FlowControl.Call:
                    blockEndStmt = new StmtContinuation(this.ctx, end.Next, false);
                    this.mappable.Add((StmtContinuation)blockEndStmt);
                    break;

                case FlowControl.Return:
                    switch (code)
                    {
                    case Code.Endfinally:
                        blockEndStmt = new StmtContinuation(this.ctx, startOfNextPart, true);
                        this.mappable.Add((StmtContinuation)blockEndStmt);
                        break;

                    case Code.Ret:
                        blockEndStmt = new StmtContinuation(this.ctx, this.endBlock, false);
                        blockInsts   = start == end?Enumerable.Empty <Instruction>() : start.GetRange(end.Previous); // Remove 'ret' from statements

                        break;

                    default:
                        blockEndStmt = null;
                        break;
                    }
                    break;

                case FlowControl.Throw:
                    blockEndStmt = null;
                    break;

                default:
                    throw new NotImplementedException("Cannot handle: " + end.OpCode.FlowControl);
                }
                var block = new StmtCil(this.ctx, blockInsts, blockEndStmt);
                this.blockMap.Add(start, new List <Stmt> {
                    block
                });
            }
        }
        protected override ICode VisitIf(StmtIf s)
        {
            var then  = (Stmt)this.Visit(s.Then);
            var @else = (Stmt)this.Visit(s.Else);

            if (then == null && @else == null)
            {
                return(null);
            }
            // Remove 'if' if condition is just true or false
            if (s.Condition.ExprType == Expr.NodeType.Literal)
            {
                if (s.Condition.IsLiteralBoolean(true))
                {
                    return(then);
                }
                if (s.Condition.IsLiteralBoolean(false))
                {
                    return(@else);
                }
            }
            // If 'then' and 'else' are identical, then remove 'if'
            if (then.DoesEqual(@else))
            {
                return(then);
            }
            // If 'if' only has an 'else' case, not a 'then' case, then swap
            if (then == null)
            {
                return(new StmtIf(s.Ctx, s.Ctx.ExprGen.NotAutoSimplify(s.Condition), @else, null));
            }
            // If both 'if' parts only contain an assignment to the same (with phi clustering) target, then turn into ternary assignment
            if (then != null && @else != null && then.StmtType == Stmt.NodeType.Assignment && @else.StmtType == Stmt.NodeType.Assignment)
            {
                var thenAssign = (StmtAssignment)then;
                var elseAssign = (StmtAssignment)@else;
                if (this.AreClustered(thenAssign.Target, elseAssign.Target))
                {
                    this.replaceVars.Add(Tuple.Create(elseAssign.Target, thenAssign.Target));
                    var ternary = new ExprTernary(s.Ctx, s.Condition, thenAssign.Expr, elseAssign.Expr);
                    return(new StmtAssignment(s.Ctx, thenAssign.Target, ternary));
                }
            }
            // If 'if' contains only 'if' then combine condition with 'and'
            if (@else == null && then.StmtType == Stmt.NodeType.If)
            {
                var thenIf = (StmtIf)then;
                if (thenIf.Else == null)
                {
                    return(new StmtIf(s.Ctx, s.Ctx.ExprGen.And(s.Condition, thenIf.Condition), thenIf.Then, null));
                }
            }
            if (then != s.Then || @else != s.Else)
            {
                return(new StmtIf(s.Ctx, s.Condition, then, @else));
            }
            else
            {
                return(s);
            }
        }
        protected override ICode VisitSwitch(StmtSwitch s)
        {
            // If switch statement contains no continuations then it doesn't need processing
            if (!VisitorFindContinuations.Any(s))
            {
                return(base.VisitSwitch(s));
            }
            var ctx = s.Ctx;

            // If any cases go to the same continuation as the default case, remove them
            if (s.Default != null && s.Default.StmtType == Stmt.NodeType.Continuation)
            {
                var defaultCont   = (StmtContinuation)s.Default;
                var sameAsDefault = s.Cases
                                    .Where(x => x.Stmt != null && x.Stmt.StmtType == Stmt.NodeType.Continuation && ((StmtContinuation)x.Stmt).To == defaultCont.To)
                                    .ToArray();
                if (sameAsDefault.Any())
                {
                    var cases = s.Cases.Except(sameAsDefault);
                    return(new StmtSwitch(ctx, s.Expr, cases, s.Default));
                }
            }
            // If multiple case statements all go the same continuation, then put them consecutively
            var groupedByTo = s.Cases
                              .Where(x => x.Stmt != null && x.Stmt.StmtType == Stmt.NodeType.Continuation)
                              .GroupBy(x => ((StmtContinuation)x.Stmt).To)
                              .Where(x => x.Count() >= 2)
                              .ToArray();

            if (groupedByTo.Any())
            {
                var cases         = s.Cases.Except(groupedByTo.SelectMany(x => x));
                var combinedCases = groupedByTo.SelectMany(x => {
                    var same      = x.ToArray();
                    var last      = same.Last();
                    var sameCases = same.Take(same.Length - 1).Select(y => new StmtSwitch.Case(y.Value, null))
                                    .Concat(new StmtSwitch.Case(last.Value, last.Stmt));
                    return(sameCases);
                });
                var allCases = cases.Concat(combinedCases).ToArray();
                return(new StmtSwitch(ctx, s.Expr, allCases, s.Default));
            }
            Func <Stmt, IEnumerable <StmtContinuation> > getSingleFinalContinuation = stmt => {
                if (stmt == null)
                {
                    return(Enumerable.Empty <StmtContinuation>());
                }
                var contCount = VisitorFindContinuations.Get(stmt).Count();
                if (contCount == 0)
                {
                    // Case contains return or throw
                    return(Enumerable.Empty <StmtContinuation>());
                }
                if (contCount == 1)
                {
                    if (stmt.StmtType == Stmt.NodeType.Continuation)
                    {
                        return(new[] { (StmtContinuation)stmt });
                    }
                    if (stmt.StmtType == Stmt.NodeType.Block)
                    {
                        var stmtBlock = (StmtBlock)stmt;
                        var last      = stmtBlock.Statements.LastOrDefault();
                        if (last != null && last.StmtType == Stmt.NodeType.Continuation)
                        {
                            return(new[] { (StmtContinuation)last });
                        }
                    }
                }
                return(new StmtContinuation[] { null });
            };
            var conts = s.Cases.Select(x => x.Stmt).Concat(s.Default).SelectMany(x => getSingleFinalContinuation(x)).ToArray();

            if (conts.All(x => x != null))
            {
                // If all cases end with a continuation to the same stmt, then put that stmt after the switch and remove all continuations
                if (conts.AllSame(x => x.To))
                {
                    Func <Stmt, Stmt> removeCont = stmt => {
                        if (stmt == null)
                        {
                            return(null);
                        }
                        switch (stmt.StmtType)
                        {
                        case Stmt.NodeType.Continuation:
                            return(new StmtBreak(ctx));

                        case Stmt.NodeType.Block:
                            var sBlock = (StmtBlock)stmt;
                            var stmts  = sBlock.Statements.ToArray();
                            if (stmts.Last().StmtType == Stmt.NodeType.Continuation)
                            {
                                stmts = stmts.Take(stmts.Length - 1).Concat(new StmtBreak(ctx)).ToArray();
                                return(new StmtBlock(ctx, stmts));
                            }
                            else
                            {
                                return(stmt);
                            }

                        default:
                            return(stmt);
                        }
                    };
                    var cases   = s.Cases.Select(x => new StmtSwitch.Case(x.Value, removeCont(x.Stmt))).ToArray();
                    var @switch = new StmtSwitch(ctx, s.Expr, cases, removeCont(s.Default));
                    return(new StmtBlock(ctx, @switch, conts[0]));
                }
                else if (this.lastChance)
                {
                    // HACK: Change it into multiple if statements
                    var multiValues = new List <int>();
                    var converted   = s.Cases.Aggregate(s.Default, (@else, @case) => {
                        multiValues.Add(@case.Value);
                        if (@case.Stmt == null)
                        {
                            return(@else);
                        }
                        else
                        {
                            var cond = multiValues.Aggregate((Expr)ctx.Literal(false), (expr, caseValue) => {
                                return(ctx.ExprGen.Or(expr, ctx.ExprGen.Equal(s.Expr, ctx.Literal(caseValue))));
                            });
                            multiValues.Clear();
                            var @if = new StmtIf(ctx, cond, @case.Stmt, @else);
                            return(@if);
                        }
                    });
                    return(converted);
                }
                // If some cases end in a continuation that itself ends in a continuation that other cases end with
                // then use an extra variable to store whether to execute the intermediate code
                // TODO: This is too specific, need a more general-purpose solution to the problem where cases
                // don't all end by going to the same place
                //var contTos = conts.Select(x => x.To).Distinct().ToArray();
                //var finalContTos = contTos.Select(x => getSingleFinalContinuation(x).Select(y => y.NullThru(z => z.To))).SelectMany(x => x).ToArray();
                //if (!finalContTos.Any(x => x == null)) {
                //    // All continuations are fully substituted
                //    var distinctFinalContTos = finalContTos.Distinct().ToArray();
                //    if (distinctFinalContTos.Length == 1) {
                //        var selector = ctx.Local(ctx.Int32);
                //        var inIfCont = contTos.Single(x => x != distinctFinalContTos[0]);
                //        var inIf = new StmtContinuation(ctx, inIfCont, false);
                //        var afterIf = new StmtContinuation(ctx, distinctFinalContTos[0], false);
                //        var allCasesTo = new StmtBlock(ctx,
                //            new StmtIf(ctx, ctx.ExprGen.Equal(selector, ctx.Literal(1)), inIf, null),
                //            afterIf);
                //        Func<Stmt, Stmt> adjustCont = stmt => {
                //            var cont = VisitorFindContinuations.Get(stmt).Single();
                //            var newCont = new StmtContinuation(ctx, allCasesTo, false);
                //            var contChanged = (Stmt)VisitorReplace.V(stmt, cont, newCont);
                //            var sValue = cont.To == inIf.To ? 1 : 0;
                //            var withSelectorSet = new StmtBlock(ctx,
                //                new StmtAssignment(ctx, selector, ctx.Literal(sValue)),
                //                contChanged);
                //            return withSelectorSet;
                //        };
                //        var cases = s.Cases.Select(x => new StmtSwitch.Case(x.Value, adjustCont(x.Stmt))).ToArray();
                //        var @switch = new StmtSwitch(ctx, s.Expr, cases, adjustCont(s.Default));
                //        return @switch;
                //    } else {
                //        throw new NotImplementedException();
                //    }
                //}
            }

            return(base.VisitSwitch(s));
        }
Пример #13
0
        public static ICode V(ICode ast)
        {
            var ctx       = ast.Ctx;
            var blockInfo = FindSuitableBlocks.GetInfo(ast);
            var bestBlock = blockInfo
                            .Where(x => !x.Value.containsLeaveProtectedRegion && x.Value.numConts <= 1)
                            .OrderBy(x => x.Value.numConts == 0 ? 1000 : x.Value.numConts)
                            .ThenBy(x => x.Value.numICodes)
                            .FirstOrDefault();

            if (bestBlock.Key == null)
            {
                return(ast);
            }
            if (bestBlock.Value.numConts > 1)
            {
                // Best block must have just one continuation
                return(ast);
            }
            Stmt addContTo = null;

            if (bestBlock.Value.numConts == 0)
            {
                addContTo = (Stmt)blockInfo.FirstOrDefault(x => x.Value.numConts == 0 && x.Key != bestBlock.Key).Key;
                if (addContTo == null)
                {
                    return(ast);
                }
            }
            var blockAst = (Stmt)bestBlock.Key;

            if (blockAst.StmtType != Stmt.NodeType.Block)
            {
                // Best block must be StmtBlock
                return(ast);
            }
            var stmtBlock = (StmtBlock)blockAst;
            var stmts     = stmtBlock.Statements.ToArray();
            var cont      = bestBlock.Value.numConts == 0 ? new StmtContinuation(ctx, addContTo, false) : (StmtContinuation)stmts.Last();

            var ifSkipContentPhi      = new ExprVarPhi(ctx);
            var ifSkipInitialVar      = ctx.Local(ctx.Boolean);
            var ifSkipContentVar      = ctx.Local(ctx.Boolean);
            var ifSkipContentPhiExprs = new List <Expr> {
                ifSkipInitialVar, ifSkipContentVar
            };
            var ifSkipReset = new StmtAssignment(ctx, ifSkipContentVar, ctx.Literal(false));

            var inIfBlock = new StmtBlock(ctx, stmts.Take(stmts.Length - (bestBlock.Value.numConts == 0 ? 0 : 1)));
            var ifBlock   = new StmtIf(ctx, ctx.ExprGen.Not(ifSkipContentPhi), inIfBlock, null);
            var newBlock  = new StmtBlock(ctx, ifBlock, ifSkipReset, cont);

            ast = VisitorReplace.V(ast, blockAst, newBlock);

            var allConts          = VisitorFindContinuationsRecursive.Get(ast);
            var contsNeedChanging = allConts.Where(x => x != cont && x.To == cont.To).ToArray();
            var contsReplaceInfo  = contsNeedChanging
                                    .Select(x => {
                var ifVar   = ctx.Local(ctx.Boolean);
                var newCont = new StmtBlock(ctx,
                                            new StmtAssignment(ctx, ifVar, ctx.Literal(true)),
                                            new StmtContinuation(ctx, newBlock, x.LeaveProtectedRegion));
                return(new { oldCont = x, ifVar, newCont });
            })
                                    .ToArray();

            foreach (var contReplace in contsReplaceInfo)
            {
                ifSkipContentPhiExprs.Add(contReplace.ifVar);
                ast = VisitorReplace.V(ast, contReplace.oldCont, contReplace.newCont);
            }
            ifSkipContentPhi.Exprs = ifSkipContentPhiExprs;

            // TODO: Shouldn't be required, but definite-assignment doesn't quite work properly, so is required
            var initalSkipVarAssignment = new StmtAssignment(ctx, ifSkipInitialVar, ctx.Literal(false));
            var newAst = new StmtBlock(ctx, initalSkipVarAssignment, (Stmt)ast);

            return(newAst);
        }
Пример #14
0
        protected override ICode VisitIf(StmtIf s)
        {
            var ctx          = s.Ctx;
            var eg           = ctx.ExprGen;
            var condition    = (Expr)this.Visit(s.Condition);
            var notCondition = eg.Not(condition);

            var assignmentsTo = this.assignmentsTo;

            this.assignmentsTo = new List <Expr>();

            this.knownTrue.Push(new List <Expr>());
            this.AddKnownTrue(condition);
            var then              = (Stmt)this.Visit(s.Then);
            var thenTrue          = this.knownTrue.Pop();
            var thenAssignmentsTo = this.assignmentsTo;

            this.assignmentsTo = new List <Expr>();
            this.knownTrue.Push(new List <Expr>());
            this.AddKnownTrue(notCondition);
            var @else             = (Stmt)this.Visit(s.Else);
            var elseTrue          = this.knownTrue.Pop();
            var elseAssignmentsTo = this.assignmentsTo;

            this.assignmentsTo = assignmentsTo.Concat(thenAssignmentsTo).Concat(elseAssignmentsTo).ToList();

            if (this.HasBlockExit(s.Then))
            {
                this.AddKnownTrue(elseTrue);
            }
            else
            {
                var known = thenTrue.Skip(1).Select(x => eg.Or(notCondition, x)).ToArray();
                this.AddKnownTrue(known);
            }
            if (this.HasBlockExit(s.Else))
            {
                this.AddKnownTrue(thenTrue);
            }
            else
            {
                var known = elseTrue.Skip(1).Select(x => eg.Or(condition, x)).ToArray();
                this.AddKnownTrue(known);
            }

            var thenAssignmentTruths = thenAssignmentsTo.Select(x => eg.Or(condition, eg.Not(x))).ToArray();

            this.AddKnownTrue(thenAssignmentTruths);
            var elseAssignmentTruths = elseAssignmentsTo.Select(x => eg.Or(notCondition, eg.Not(x))).ToArray();

            this.AddKnownTrue(elseAssignmentTruths);

            if (condition != s.Condition || then != s.Then || @else != s.Else)
            {
                return(new StmtIf(ctx, condition, then, @else));
            }
            else
            {
                return(s);
            }
        }