Пример #1
0
        private Tuple <State, bool> GetStateAfterStatement(StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <int, string> > finallyStack, State returnState)
        {
            JsStatement next;

            if (location.Index < location.Block.Statements.Count - 1)
            {
                next = location.Block.Statements[location.Index + 1];
            }
            else if (!stack.IsEmpty)
            {
                var tos = stack.Peek();
                next = tos.Block.Statements[tos.Index];
            }
            else
            {
                next = null;
            }

            if (next is JsLabelledStatement)
            {
                return(Tuple.Create(GetOrCreateStateForLabel((next as JsLabelledStatement).Label, finallyStack), false));
            }
            else if (next != null)
            {
                return(Tuple.Create(CreateNewStateValue(finallyStack), true));
            }
            else
            {
                return(Tuple.Create(returnState, false));
            }
        }
Пример #2
0
        private bool HandleAwaitStatement(JsAwaitStatement 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).Item1;
            bool createDummyState = false;

            if (stateAfter.StateValue == returnState.StateValue)
            {
                stateAfter       = CreateNewStateValue(currentState.FinallyStack);
                createDummyState = true;                        // We must never return to our parent state after an await because
            }

            currentBlock.Add(new JsSetNextStateStatement(stateAfter.StateValue));
            currentBlock.Add(new JsExpressionStatement(JsExpression.Invocation(JsExpression.MemberAccess(stmt.Awaiter, stmt.OnCompletedMethodName), JsExpression.Identifier(_stateMachineMethodName))));
            if (_needDoFinallyBlocksVariable)
            {
                currentBlock.Add(new JsExpressionStatement(JsExpression.Assign(JsExpression.Identifier(_doFinallyBlocksVariableName), JsExpression.False)));
            }
            currentBlock.Add(new JsReturnStatement());

            if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1)
            {
                Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter, returnState);
            }
            if (createDummyState)
            {
                Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(new JsBlockStatement(new JsBlockStatement(new JsStatement[0], mergeWithParent: true)), 0)), breakStack, continueStack, stateAfter, returnState);
            }

            return(false);
        }
Пример #3
0
        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)
        {
            if (NeedsBreakBeforeLoop(currentBlock))
            {
                // 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(new JsIfStatement(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));

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

                return(false);
            }
        }
        private bool HandleYieldReturnStatement(JsYieldStatement 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);

            currentBlock.Add(_makeSetCurrent(stmt.Value));
            currentBlock.Add(new JsSetNextStateStatement(stateAfter.Item1.StateValue));
            currentBlock.Add(JsStatement.Return(JsExpression.True));

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

            return(false);
        }
Пример #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);
        }
Пример #6
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);

            var thenPart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Then, 0)), breakStack, continueStack, currentState, stateAfter.Item1);
            var elsePart = stmt.Else != null?Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Else, 0)), breakStack, continueStack, currentState, stateAfter.Item1) : null;

            currentBlock.Add(new JsIfStatement(stmt.Test, new JsBlockStatement(thenPart), elsePart != null ? new JsBlockStatement(elsePart) : null));
            if (elsePart == null)
            {
                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);
            }
        }
        private bool HandleForStatement(JsForStatement 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 && (stmt.InitStatement is JsEmptyStatement || location.AfterForInitializer)))
            {
                // We have to create a new block for the statement.
                var topOfLoopState = CreateNewStateValue(currentState.FinallyStack);
                Enqueue(stack.Push(new StackEntry(location.Block, location.Index, true)), breakStack, continueStack, topOfLoopState, returnState);
                if (!(stmt.InitStatement is JsEmptyStatement))
                {
                    currentBlock.Add(stmt.InitStatement);
                }
                currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState));
                return(false);
            }
            else
            {
                var iteratorState  = (stmt.IteratorExpression != null ? CreateNewStateValue(currentState.FinallyStack) : currentState);
                var afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);

                if (stmt.ConditionExpression != null)
                {
                    currentBlock.Add(JsStatement.If(JsExpression.LogicalNot(stmt.ConditionExpression), new JsGotoStateStatement(afterLoopState.Item1, currentState), null));
                }
                string 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, iteratorState)), currentState, iteratorState, false, false));

                if (stmt.IteratorExpression != null)
                {
                    Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(stmt.IteratorExpression), 0)), breakStack, continueStack, iteratorState, currentState);
                }

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

                return(false);
            }
        }
        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)
        {
            if (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();
                _finallyHandlers.Add(Tuple.Create(handlerName, FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label) ? new FinalizerRewriter(_stateVariableName, _labelStates).Process(new JsBlockStatement(ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack))) : stmt.Finally));
                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, innerState, stateBeforeFinally));

                Enqueue(ImmutableStack<StackEntry>.Empty.Push(new StackEntry(new JsBlockStatement(new JsBlockStatement(new JsStatement[0], true)), 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 (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(new JsTryStatement(new JsTryStatement(stmt.GuardedStatement, stmt.Catch, null), null, stmt.Finally), location, stack, breakStack, continueStack, currentState, returnState, currentBlock);
            }
            else {
                var rewriter = new NestedJumpStatementRewriter(breakStack, continueStack, currentState, _exitState.Value);
                var guarded = FindInterestingConstructsVisitor.Analyze(stmt.GuardedStatement, InterestingConstruct.Label)
                              ? new JsBlockStatement(ProcessInner(stmt.GuardedStatement, breakStack, continueStack, currentState.FinallyStack))
                              : rewriter.Process(stmt.GuardedStatement);

                JsCatchClause @catch;
                if (stmt.Catch != null) {
                    if (FindInterestingConstructsVisitor.Analyze(stmt.Catch.Body, InterestingConstruct.Label)) {
                        @catch = new JsCatchClause(stmt.Catch.Identifier, new JsBlockStatement(ProcessInner(stmt.Catch.Body, breakStack, continueStack, currentState.FinallyStack)));
                    }
                    else {
                        var body = rewriter.Process(stmt.Catch.Body);
                        @catch = ReferenceEquals(body, stmt.Catch.Body) ? stmt.Catch : new JsCatchClause(stmt.Catch.Identifier, body);
                    }
                }
                else
                    @catch = null;

                JsBlockStatement @finally;
                if (stmt.Finally != null) {
                    if (FindInterestingConstructsVisitor.Analyze(stmt.Finally, InterestingConstruct.Label))
                        @finally = new JsBlockStatement(ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack));
                    else
                        @finally = rewriter.Process(stmt.Finally);
                }
                else
                    @finally = null;

                currentBlock.Add(new JsTryStatement(guarded, @catch, @finally));
                return true;
            }
        }
 private ImmutableStack <StackEntry> PushFollowing(ImmutableStack <StackEntry> stack, StackEntry location)
 {
     return(location.Index < location.Block.Statements.Count - 1 ? stack.Push(new StackEntry(location.Block, location.Index + 1)) : stack);
 }
Пример #11
0
 private bool NeedsBreakBeforeForLoop(IList <JsStatement> currentBlock, JsForStatement stmt, StackEntry location)
 {
     if (NeedsBreakBeforeLoop(currentBlock))
     {
         return(true);
     }
     else if (!(stmt.InitStatement is JsEmptyStatement) && !location.AfterForInitializer)
     {
         return(true);                   // If we have an initializer, the current location must state explicitly to have handled it.
     }
     else
     {
         return(false);
     }
 }
		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.State, 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.State, 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.State, currentState));

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

			return true;
		}
Пример #13
0
		private bool HandleAwaitStatement(JsAwaitStatement 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).Item1;
			bool createDummyState = false;
			if (stateAfter.StateValue == returnState.StateValue) {
				stateAfter = CreateNewStateValue(currentState.FinallyStack);
				createDummyState = true;	// We must never return to our parent state after an await because 
			}

			currentBlock.Add(new JsSetNextStateStatement(stateAfter.StateValue));
			currentBlock.Add(JsExpression.Invocation(JsExpression.Member(stmt.Awaiter, stmt.OnCompletedMethodName), JsExpression.Identifier(_stateMachineMethodName)));
			if (_needDoFinallyBlocksVariable)
				currentBlock.Add(JsExpression.Assign(JsExpression.Identifier(_doFinallyBlocksVariableName), JsExpression.False));
			currentBlock.Add(JsStatement.Return());

			if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1) {
				Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter, returnState);
			}
			if (createDummyState) {
				Enqueue(ImmutableStack<StackEntry>.Empty.Push(new StackEntry(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 0)), breakStack, continueStack, stateAfter, returnState);
			}

			return false;
		}
Пример #14
0
		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;
			}
		}
Пример #15
0
		private bool HandleForStatement(JsForStatement 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 && (stmt.InitStatement is JsEmptyStatement || location.AfterForInitializer))) {
				// We have to create a new block for the statement.
				var topOfLoopState = CreateNewStateValue(currentState.FinallyStack);
				Enqueue(stack.Push(new StackEntry(location.Block, location.Index, true)), breakStack, continueStack, topOfLoopState, returnState);
				if (!(stmt.InitStatement is JsEmptyStatement))
					currentBlock.Add(stmt.InitStatement);
				currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState));
				return false;
			}
			else {
				var iteratorState = (stmt.IteratorExpression != null ? CreateNewStateValue(currentState.FinallyStack) : currentState);
				var afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState);

				if (stmt.ConditionExpression != null)
					currentBlock.Add(JsStatement.If(JsExpression.LogicalNot(stmt.ConditionExpression), new JsGotoStateStatement(afterLoopState.Item1, currentState), null));
				string 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, iteratorState)), currentState, iteratorState, false, false));

				if (stmt.IteratorExpression != null) {
					Enqueue(ImmutableStack<StackEntry>.Empty.Push(new StackEntry(JsStatement.Block(stmt.IteratorExpression), 0)), breakStack, continueStack, iteratorState, currentState);
				}

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

				return false;
			}
		}
 private ImmutableStack<StackEntry> PushFollowing(ImmutableStack<StackEntry> stack, StackEntry location)
 {
     return location.Index < location.Block.Statements.Count - 1 ? stack.Push(new StackEntry(location.Block, location.Index + 1)) : stack;
 }
        private Tuple<State, bool> GetStateAfterStatement(StackEntry location, ImmutableStack<StackEntry> stack, ImmutableStack<Tuple<int, string>> finallyStack, State returnState)
        {
            JsStatement next;
            if (location.Index < location.Block.Statements.Count - 1) {
                next = location.Block.Statements[location.Index + 1];
            }
            else if (!stack.IsEmpty) {
                var tos = stack.Peek();
                next = tos.Block.Statements[tos.Index];
            }
            else
                next = null;

            if (next is JsLabelledStatement) {
                return Tuple.Create(GetOrCreateStateForLabel((next as JsLabelledStatement).Label, finallyStack), false);
            }
            else if (next != null) {
                return Tuple.Create(CreateNewStateValue(finallyStack), true);
            }
            else {
                return Tuple.Create(returnState, false);
            }
        }
        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);
            }
        }
        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(new JsVariableDeclarationStatement(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(new JsBlockStatement(origBody), 0)), breakStack, continueStack, currentFallthroughState.Value, stateAfter.Item1);
                }
                else {
                    body = Handle(ImmutableStack<StackEntry>.Empty.Push(new StackEntry(new JsBlockStatement(origBody), 0)), breakStack, continueStack, currentState, nextFallthroughState ?? stateAfter.Item1);
                }

                if (clause.Values.Any(v => v == null)) {
                    defaultClause = new JsBlockStatement(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, new JsBlockStatement(body)));
                }

                currentFallthroughState = nextFallthroughState;
            }
            clauses.Reverse();

            currentBlock.Add(clauses.Where(c => c.Item1 != null).Aggregate(defaultClause, (o, n) => new JsIfStatement(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 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);

            var thenPart = Handle(ImmutableStack<StackEntry>.Empty.Push(new StackEntry(stmt.Then, 0)), breakStack, continueStack, currentState, stateAfter.Item1);
            var elsePart = stmt.Else != null ? Handle(ImmutableStack<StackEntry>.Empty.Push(new StackEntry(stmt.Else, 0)), breakStack, continueStack, currentState, stateAfter.Item1) : null;

            currentBlock.Add(new JsIfStatement(stmt.Test, new JsBlockStatement(thenPart), elsePart != null ? new JsBlockStatement(elsePart) : null));
            if (elsePart == null)
                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)
        {
            if (currentBlock.Count > 0) {
                // 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));

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

                return false;
            }
        }
        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)
        {
            if (currentBlock.Count > 0) {
                // 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(new JsIfStatement(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));

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

                return false;
            }
        }
        private bool HandleYieldReturnStatement(JsYieldStatement 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);

            currentBlock.Add(new JsExpressionStatement(_makeSetCurrent(stmt.Value)));
            currentBlock.Add(new JsSetNextStateStatement(stateAfter.Item1.StateValue));
            currentBlock.Add(new JsReturnStatement(JsExpression.True));

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

            return false;
        }
        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 NeedsBreakBeforeForLoop(IList<JsStatement> currentBlock, JsForStatement stmt, StackEntry location)
 {
     if (NeedsBreakBeforeLoop(currentBlock))
         return true;
     else if (!(stmt.InitStatement is JsEmptyStatement) && !location.AfterForInitializer)
         return true;	// If we have an initializer, the current location must state explicitly to have handled it.
     else
         return false;
 }