Exemplo n.º 1
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;
			}
		}
 private JsBlockStatement Process(JsBlockStatement statement, bool isIteratorBlock)
 {
     _stateVariableName = _allocateStateVariable();
     _nextStateIndex = 0;
     _isIteratorBlock = isIteratorBlock;
     _processedStates = new HashSet<State>();
     _labelStates = new Dictionary<string, State>();
     _finallyHandlers = new List<Tuple<string, JsBlockStatement>>();
     _allStates = new List<State>();
     _remainingBlocks = new Queue<RemainingBlock>();
     _exitState = null;
     var body = ProcessInner(statement, ImmutableStack<Tuple<string, State>>.Empty, ImmutableStack<Tuple<string, State>>.Empty, ImmutableStack<Tuple<int, string>>.Empty);
     body.RemoveAt(0);	// Remove the initial assignment of the state. This will instead happen in the statement that declares the variables for this state machine.
     if (_isIteratorBlock)
         body.Add(new JsReturnStatement(JsExpression.False));
     var resultBody = new FinalizerRewriter(_stateVariableName, _labelStates).Process(new JsBlockStatement(body));
     return resultBody;
 }
Exemplo n.º 3
0
		private JsBlockStatement Process(JsBlockStatement statement, bool isIteratorBlock, bool isAsync) {
			_stateVariableName = _allocateStateVariable();
			_nextStateIndex = 0;
			_isIteratorBlock = isIteratorBlock;
			_isAsync = isAsync;
			_processedStates = new HashSet<State>();
			_labelStates = new Dictionary<string, State>();
			_finallyHandlers = new List<Tuple<string, JsBlockStatement>>();
			_allStates = new List<State>();
			_remainingBlocks = new Queue<RemainingBlock>();
			_exitState = null;
			_childStates = new Dictionary<int, List<int>>();
			var body = ProcessInner(statement, ImmutableStack<Tuple<string, State>>.Empty, ImmutableStack<Tuple<string, State>>.Empty, ImmutableStack<Tuple<int, string>>.Empty, null).Item1;

			if (_isIteratorBlock)
				body.Add(JsStatement.Return(JsExpression.False));
			var resultBody = new FinalizerRewriter(_stateVariableName, _labelStates).Process(JsStatement.Block(body));
			return resultBody;
		}