Esempio n. 1
0
        public JsTryStatement(JsStatement guardedStatement, JsCatchClause catchClause, JsStatement @finally)
        {
            if (guardedStatement == null)
            {
                throw new ArgumentException("guardedStatement");
            }
            if (catchClause == null && @finally == null)
            {
                throw new ArgumentException("Either catchClause or finally (or both) must be specified");
            }

            GuardedStatement = EnsureBlock(guardedStatement);
            Catch            = catchClause;
            Finally          = EnsureBlock(@finally);
        }
        public override void VisitTryCatchStatement(TryCatchStatement tryCatchStatement)
        {
            var tryBlock = CreateInnerCompiler().Compile(tryCatchStatement.TryBlock);
            JsCatchClause catchClause = null;
            if (tryCatchStatement.CatchClauses.Count > 0) {
                var oldVariableForRethrow = _currentVariableForRethrow;

                _currentVariableForRethrow = CreateTemporaryVariable(_compilation.FindType(KnownTypeCode.Object), tryCatchStatement.CatchClauses.First().GetRegion());
                string catchVariableName = _variables[_currentVariableForRethrow].Name;

                var catchClauses = tryCatchStatement.CatchClauses.ToList();
                var systemException = _compilation.FindType(KnownTypeCode.Exception);
                RemoveCatchClausesAfterExceptionType(catchClauses, systemException);

                bool lastIsCatchall = (catchClauses[catchClauses.Count - 1].Type.IsNull || _resolver.Resolve(catchClauses[catchClauses.Count - 1].Type).Type.Equals(systemException));
                JsStatement current = lastIsCatchall
                                    ? CompileCatchClause(new LocalResolveResult(_currentVariableForRethrow), catchClauses[catchClauses.Count - 1], true, catchClauses.Count == 1)
                                    : new JsBlockStatement(new JsThrowStatement(JsExpression.Identifier(catchVariableName)));

                for (int i = catchClauses.Count - (lastIsCatchall ? 2 : 1); i >= 0; i--) {
                    var test = _runtimeLibrary.TypeIs(JsExpression.Identifier(catchVariableName), _currentVariableForRethrow.Type, _resolver.Resolve(catchClauses[i].Type).Type);
                    current = new JsIfStatement(test, CompileCatchClause(new LocalResolveResult(_currentVariableForRethrow), catchClauses[i], false, catchClauses.Count == 1), current);
                }

                if (!lastIsCatchall || catchClauses.Count > 1) {
                    // We need to wrap the exception.
                    current = new JsBlockStatement(new JsExpressionStatement(JsExpression.Assign(JsExpression.Identifier(catchVariableName), _runtimeLibrary.MakeException(JsExpression.Identifier(catchVariableName)))), current);
                }

                catchClause = new JsCatchClause(catchVariableName, current);
                _currentVariableForRethrow = oldVariableForRethrow;
            }

            var finallyBlock = (!tryCatchStatement.FinallyBlock.IsNull ? CreateInnerCompiler().Compile(tryCatchStatement.FinallyBlock) : null);

            _result.Add(new JsTryStatement(tryBlock, catchClause, finallyBlock));
        }
		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 = new JsBlockStatement(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(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 (_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(new JsTryStatement(new JsTryStatement(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    = new JsBlockStatement(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 = new JsCatchClause(stmt.Catch.Identifier, new JsBlockStatement(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1)));
					}
					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)) {
						var inner = ProcessInner(stmt.Finally, breakStack, continueStack, currentState.FinallyStack, null);
						@finally = new JsBlockStatement(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 = new JsBlockStatement(new JsIfStatement(JsExpression.Identifier(_doFinallyBlocksVariableName), @finally, null));
					}
				}
				else
					@finally = null;

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

				currentBlock.Add(new JsTryStatement(guarded, @catch, @finally));
				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)
        {
            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;
            }
        }
Esempio n. 5
0
 public static JsTryStatement Try(JsStatement guardedStatement, JsCatchClause catchClause, JsStatement @finally)
 {
     return(new JsTryStatement(guardedStatement, catchClause, @finally));
 }