예제 #1
0
        private JsExpression GenerateStructEqualsMethod(ITypeDefinition type, string typeVariableName)
        {
            var o     = JsExpression.Identifier("o");
            var parts = new List <JsExpression>();

            foreach (var f in type.Fields.Where(f => !f.IsStatic))
            {
                var expr = GenerateFieldCompare(f, o);
                if (expr != null)
                {
                    parts.Add(expr);
                }
            }

            JsExpression typeCompare = JsExpression.Invocation(JsExpression.Member(_systemScript, "isInstanceOfType"), o, JsExpression.Identifier(typeVariableName));

            if (parts.Count == 0)
            {
                return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Return(typeCompare)));
            }
            else
            {
                return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Block(
                                                           JsStatement.If(JsExpression.LogicalNot(typeCompare),
                                                                          JsStatement.Return(JsExpression.False),
                                                                          null
                                                                          ),
                                                           JsStatement.Return(parts.Aggregate((old, p) => old == null ? p : JsExpression.LogicalAnd(old, p)))
                                                           )));
            }
        }
        private bool HandleWhileStatement(JsWhileStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement)
        {
            if (!isFirstStatement)
            {
                // We have to create a new block for the statement.
                var topOfLoopState = CreateNewStateValue(currentState.FinallyStack);
                Enqueue(stack.Push(location), breakStack, continueStack, topOfLoopState, returnState);
                currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState));
                return(false);
            }
            else
            {
                var afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);

                currentBlock.Add(JsStatement.If(JsExpression.LogicalNot(stmt.Condition), new JsGotoStateStatement(afterLoopState.Item1, currentState), null));
                var currentName = GetLabelForState(currentState);
                currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Body, 0)), breakStack.Push(Tuple.Create(currentName, afterLoopState.Item1)), continueStack.Push(Tuple.Create(currentName, currentState)), currentState, currentState, false, false));

                if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1)
                {
                    Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState);
                }

                return(false);
            }
        }
예제 #3
0
 public void IfStatementIsCorrectlyOutput()
 {
     AssertCorrect(JsStatement.If(JsExpression.True, JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), null),
                   "if (true) {\n\ti = 0;\n}\n");
     AssertCorrect(JsStatement.If(JsExpression.True, JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(1))),
                   "if (true) {\n\ti = 0;\n}\nelse {\n\ti = 1;\n}\n");
 }
 public void IfAndElseIfStatementsAreChained()
 {
     AssertCorrect(JsStatement.If(JsExpression.True, JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), null),
                   "if(true){i=0;}");
     AssertCorrect(JsStatement.If(JsExpression.Identifier("a"), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)),
                                  JsStatement.If(JsExpression.Identifier("b"), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(1)),
                                                 JsStatement.If(JsExpression.Identifier("c"), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(2)), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(3))))),
                   "if(a){i=0;}else if(b){i=1;}else if(c){i=2;}else{i=3;}");
 }
예제 #5
0
        private bool HandleIfStatement(JsIfStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock)
        {
            var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);

            IList <JsStatement> thenPart, elsePart;

            if (stmt.Then.Statements.Count == 0)
            {
                thenPart = EmptyList <JsStatement> .Instance;
            }
            else
            {
                thenPart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Then, 0)), breakStack, continueStack, currentState, stateAfter.Item1, false, false);
            }

            if (stmt.Else == null)
            {
                elsePart = null;
            }
            else if (stmt.Else.Statements.Count == 0)
            {
                elsePart = EmptyList <JsStatement> .Instance;
            }
            else
            {
                elsePart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Else, 0)), breakStack, continueStack, currentState, stateAfter.Item1, false, false);
            }

            currentBlock.Add(JsStatement.If(stmt.Test, JsStatement.Block(thenPart), elsePart != null ? JsStatement.Block(elsePart) : null));
            if (thenPart.Count == 0 || elsePart == null || elsePart.Count == 0)
            {
                currentBlock.Add(new JsGotoStateStatement(stateAfter.Item1, currentState));
            }

            if (stateAfter.Item2)
            {
                Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState);
                return(false);
            }

            return(true);
        }
        private bool HandleDoWhileStatement(JsDoWhileStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement)
        {
            if (!isFirstStatement)
            {
                // We have to create a new block for the statement.
                var topOfLoopState = CreateNewStateValue(currentState.FinallyStack);
                Enqueue(stack.Push(location), breakStack, continueStack, topOfLoopState, returnState);
                currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState));
                return(false);
            }
            else
            {
                var beforeConditionState = CreateNewStateValue(currentState.FinallyStack);
                Tuple <State, bool> afterLoopState;
                string currentName = GetLabelForState(currentState);
                if (new ContainsBreakVisitor().Analyze(stmt.Body, currentName))
                {
                    afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);
                    breakStack     = breakStack.Push(Tuple.Create(currentName, afterLoopState.Item1));
                }
                else
                {
                    afterLoopState = Tuple.Create(returnState, false);
                }

                currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Body, 0)), breakStack, continueStack.Push(Tuple.Create(GetLabelForState(currentState), beforeConditionState)), currentState, beforeConditionState, false, false));

                if (afterLoopState.Item2)
                {
                    Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState);
                    Enqueue(stack.Push(new StackEntry(JsStatement.Block(JsStatement.If(stmt.Condition, new JsGotoStateStatement(currentState, currentState), null)), 0)), breakStack, continueStack, beforeConditionState, afterLoopState.Item1);
                }
                else
                {
                    Enqueue(PushFollowing(stack, location).Push(new StackEntry(JsStatement.Block(JsStatement.If(stmt.Condition, new JsGotoStateStatement(currentState, currentState), null)), 0)), breakStack, continueStack, beforeConditionState, returnState);
                }

                return(false);
            }
        }
예제 #7
0
        public void CanAnalyzeStatements()
        {
            var asm = Common.CreateMockAssembly();
            var t1  = Common.CreateMockTypeDefinition("Type", asm);
            var t2  = Common.CreateMockTypeDefinition("Type", asm);
            var t3  = Common.CreateMockTypeDefinition("Type", asm);

            var ast = new JsStatement[] {
                JsStatement.If(JsExpression.Member(new JsTypeReferenceExpression(t1), "X"), JsStatement.Block(
                                   JsExpression.Add(new JsTypeReferenceExpression(t2), new JsTypeReferenceExpression(t3))
                                   ),
                               null),
                JsExpression.Add(JsExpression.Number(1), new JsTypeReferenceExpression(t1)),
            };

            var refs = TypeReferenceFinder.Analyze(ast);

            Assert.That(refs, Has.Count.EqualTo(3));
            Assert.That(refs.Contains(t1));
            Assert.That(refs.Contains(t2));
            Assert.That(refs.Contains(t3));
        }
        private bool HandleSwitchStatement(JsSwitchStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock)
        {
            var          stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);
            JsExpression expression = stmt.Expression;

            if (_isExpressionComplexEnoughForATemporaryVariable(expression))
            {
                string newName = _allocateTempVariable();
                currentBlock.Add(JsStatement.Var(newName, expression));
                expression = JsExpression.Identifier(newName);
            }

            var         clauses                 = new List <Tuple <JsExpression, JsBlockStatement> >();
            JsStatement defaultClause           = null;
            State?      currentFallthroughState = null;

            for (int i = 0; i < stmt.Sections.Count; i++)
            {
                var clause = stmt.Sections[i];

                var origBody = new List <JsStatement>();
                origBody.AddRange(clause.Body.Statements);

                State?nextFallthroughState;

                if (i < stmt.Sections.Count - 1 && (origBody.Count == 0 || IsNextStatementReachable(origBody[origBody.Count - 1])))
                {
                    // Fallthrough
                    var nextBody = stmt.Sections[i + 1].Body.Statements;
                    if (nextBody.Count > 0 && nextBody[0] is JsLabelledStatement)
                    {
                        nextFallthroughState = GetOrCreateStateForLabel(((JsLabelledStatement)nextBody[0]).Label, currentState.FinallyStack);
                    }
                    else
                    {
                        nextFallthroughState = CreateNewStateValue(currentState.FinallyStack);
                    }
                }
                else
                {
                    nextFallthroughState = null;
                }

                breakStack = breakStack.Push(Tuple.Create(GetLabelForState(currentState), stateAfter.Item1));

                IList <JsStatement> body;
                if (currentFallthroughState != null)
                {
                    body = new List <JsStatement>();
                    body.Add(new JsGotoStateStatement(currentFallthroughState.Value, currentState));
                    Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(origBody), 0)), breakStack, continueStack, currentFallthroughState.Value, stateAfter.Item1);
                }
                else
                {
                    body = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(origBody), 0)), breakStack, continueStack, currentState, nextFallthroughState ?? stateAfter.Item1, false, false);
                }

                if (clause.Values.Any(v => v == null))
                {
                    defaultClause = JsStatement.Block(body);
                }
                else
                {
                    JsExpression test = clause.Values.Select(v => JsExpression.Same(expression, v)).Aggregate((o, e) => o != null ? JsExpression.LogicalOr(o, e) : e);
                    clauses.Add(Tuple.Create(test, JsStatement.Block(body)));
                }

                currentFallthroughState = nextFallthroughState;
            }
            clauses.Reverse();

            currentBlock.Add(clauses.Where(c => c.Item1 != null).Aggregate(defaultClause, (o, n) => JsStatement.If(n.Item1, n.Item2, o)));
            currentBlock.Add(new JsGotoStateStatement(stateAfter.Item1, currentState));

            if (stateAfter.Item2)
            {
                Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState);
                return(false);
            }

            return(true);
        }
        private bool HandleTryStatement(JsTryStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock, bool isFirstStatement)
        {
            if (_isIteratorBlock && (FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement, InterestingConstruct.YieldReturn) || (stmt.Finally != null && stmt.Catch == null && !currentState.FinallyStack.IsEmpty)))
            {
                if (stmt.Catch != null)
                {
                    throw new InvalidOperationException("Cannot yield return from try with catch");
                }
                string           handlerName = _allocateFinallyHandler();
                JsBlockStatement handler;
                if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label))
                {
                    var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, currentState.StateValue);
                    handler = JsStatement.Block(new[]  { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1));
                    handler = new FinalizerRewriter(_stateVariableName, _labelStates).Process(handler);
                }
                else
                {
                    handler = stmt.Finally;
                }

                _finallyHandlers.Add(Tuple.Create(handlerName, handler));
                var stateAfter         = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);
                var innerState         = CreateNewStateValue(currentState.FinallyStack, handlerName);
                var stateBeforeFinally = CreateNewStateValue(innerState.FinallyStack);
                currentBlock.Add(new JsSetNextStateStatement(innerState.StateValue));
                currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.GuardedStatement, 0)), breakStack, continueStack, new State(currentState.LoopLabelName, currentState.StateValue, innerState.FinallyStack), stateBeforeFinally, false, false));

                Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 0)), breakStack, continueStack, stateBeforeFinally, stateAfter.Item1);
                if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1)
                {
                    Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState);
                }
                return(false);
            }
            else if (_isIteratorBlock && stmt.Finally != null && !currentState.FinallyStack.IsEmpty)
            {
                // This is necessary to special-case in order to ensure that the inner finally block is executed before all outer ones.
                return(HandleTryStatement(JsStatement.Try(JsStatement.Try(stmt.GuardedStatement, stmt.Catch, null), null, stmt.Finally), location, stack, breakStack, continueStack, currentState, returnState, currentBlock, isFirstStatement));
            }
            else
            {
                var rewriter = new NestedStatementFixer(breakStack, continueStack, currentState, _exitState.Value, _makeSetResult);
                JsBlockStatement guarded;
                var guardedConstructs = FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement);
                if ((guardedConstructs & (InterestingConstruct.Label | InterestingConstruct.Await)) != InterestingConstruct.None)
                {
                    if (!isFirstStatement)
                    {
                        var sv = CreateNewStateValue(currentState.FinallyStack);
                        Enqueue(stack.Push(location), breakStack, continueStack, sv, returnState);
                        currentBlock.Add(new JsGotoStateStatement(sv, currentState));
                        return(false);
                    }

                    var inner = ProcessInner(stmt.GuardedStatement, breakStack, continueStack, currentState.FinallyStack, currentState.StateValue);
                    guarded = JsStatement.Block(inner.Item1);
                    currentBlock.Add(new JsSetNextStateStatement(inner.Item2));
                }
                else
                {
                    guarded = rewriter.Process(stmt.GuardedStatement);
                }

                JsCatchClause @catch;
                if (stmt.Catch != null)
                {
                    if (FindInterestingConstructsVisitor.Analyze(stmt.Catch.Body, InterestingConstruct.Label))
                    {
                        var inner = ProcessInner(stmt.Catch.Body, breakStack, continueStack, currentState.FinallyStack, null);
                        @catch = JsStatement.Catch(stmt.Catch.Identifier, JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1)));
                    }
                    else
                    {
                        var body = rewriter.Process(stmt.Catch.Body);
                        @catch = ReferenceEquals(body, stmt.Catch.Body) ? stmt.Catch : JsStatement.Catch(stmt.Catch.Identifier, body);
                    }
                }
                else
                {
                    @catch = null;
                }

                JsBlockStatement @finally;
                if (stmt.Finally != null)
                {
                    if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label))
                    {
                        var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, null);
                        @finally = JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1));
                    }
                    else
                    {
                        @finally = rewriter.Process(stmt.Finally);
                    }

                    if ((guardedConstructs & InterestingConstruct.Await) != InterestingConstruct.None)
                    {
                        // Wrap the finally block inside an 'if (doFinallyBlocks) {}'
                        @finally = JsStatement.Block(JsStatement.If(JsExpression.Identifier(_doFinallyBlocksVariableName), @finally, null));
                    }
                }
                else
                {
                    @finally = null;
                }

                if (currentBlock.Count > 0 && _childStates.ContainsKey(currentState.StateValue))
                {
                    var newBlock = JsStatement.If(JsExpression.Same(JsExpression.Identifier(_stateVariableName), JsExpression.Number(currentState.StateValue)), JsStatement.Block(currentBlock), null);
                    currentBlock.Clear();
                    currentBlock.Add(newBlock);
                }

                currentBlock.Add(JsStatement.Try(guarded, @catch, @finally));
                return(true);
            }
        }
        public virtual JsStatement VisitIfStatement(JsIfStatement statement, TData data)
        {
            var test  = VisitExpression(statement.Test, data);
            var then  = VisitStatement(statement.Then, data);
            var @else = statement.Else != null?VisitStatement(statement.Else, data) : null;

            return(ReferenceEquals(test, statement.Test) && ReferenceEquals(then, statement.Then) && ReferenceEquals(@else, statement.Else) ? statement : JsStatement.If(test, then, @else));
        }