Пример #1
0
        public override void VisitUsingStatement(UsingStatementSyntax node)
        {
            var afterTry        = GetNextState();
            var newTryStatement = Js.Try();

            // Keep track of exception, if any, so we can rethrow
            var exceptionIdentifier = HoistVariable(new LiftedVariableKey("$usingex"));

            // Identifier for caught exception
            var caughtExceptionIdentifier = UniqueName("$caughtex");

            // Hoist the variable into a field
            var disposables = new List <JsExpression>();

            if (node.Declaration != null)
            {
                foreach (var variable in node.Declaration.Variables)
                {
                    var symbol     = (ILocalSymbol)Transformer.Model.GetDeclaredSymbol(variable);
                    var identifier = HoistVariable(new LiftedVariableKey(variable.Identifier, symbol));
                    var name       = identifier.GetReference();
                    disposables.Add(name);
                    CurrentState.Add(name.Assign((JsExpression)variable.Initializer.Value.Accept(Transformer)).Express());
                }
            }
            if (node.Expression != null)
            {
                var identifier = Js.Reference(UniqueName("$using"));
                disposables.Add(identifier);
                CurrentState.Add(identifier.Assign((JsExpression)node.Expression.Accept(Transformer)).Express());
            }

            var tryState = NewSubstate();

            GotoState(tryState);

            var finallyState = GetNextState();

            CurrentState = finallyState;
            foreach (var disposable in disposables)
            {
                CurrentState.Add(disposable.Member("Dispose").Invoke().Express());
            }
            CurrentState.Add(Js.If(exceptionIdentifier.GetReference().NotEqualTo(Js.Null()), Js.Throw(exceptionIdentifier.GetReference())));
            GotoState(afterTry);
            newTryStatement.Catch      = Js.Catch(Js.Variable(caughtExceptionIdentifier));
            newTryStatement.Catch.Body = Js.Block(
                new[] { exceptionIdentifier.GetReference().Assign(Js.Reference(caughtExceptionIdentifier)).Express() }
                .Concat(GotoStateStatements(finallyState))
                .ToArray()
                );

            tryState.Wrap = switchStatement =>
            {
                newTryStatement.Body = Js.Block(switchStatement);
                return(newTryStatement);
            };

            StartSubstate(tryState);
            AcceptStatement(node.Statement);
            GotoState(finallyState);
            EndSubstate();

            CurrentState = afterTry;
        }
Пример #2
0
        public override void VisitTryStatement(TryStatementSyntax node)
        {
            var afterTry        = GetNextState();
            var newTryStatement = Js.Try();

            var tryState = NewSubstate();

            GotoState(tryState);

            // Keep track of exception, if any, so we can rethrow
            var exceptionIdentifier = HoistVariable(new LiftedVariableKey("$ex"));
            var exceptionVariable   = UniqueName("$caughtex");

            State finallyState = node.Finally == null ? null : GetNextState();

            // Declare a block to store all the catch statements the try statement's only catch clause. (No
            // type-specific catch clauses in Javascript
            var catchBlock = Js.Block();

            // Make sure that the exception is stored in a variable accessible to the entire state machine.
            catchBlock.Express(exceptionIdentifier.GetReference().Assign(Js.Reference(exceptionVariable)));

            foreach (var catchClause in node.Catches)
            {
                // Get the symbol that represents the exception declaration (identifier and type)
                var symbol        = Transformer.Model.GetDeclaredSymbol(catchClause.Declaration);
                var exceptionType = symbol == null ? null : symbol.Type;
                if (exceptionType == null && catchClause.Declaration != null && catchClause.Declaration.Type != null)
                {
                    exceptionType = (ITypeSymbol)Transformer.Model.GetSymbolInfo(catchClause.Declaration.Type).Symbol;
                }

                // True if it is actually declaring the variable (as opposed to a catch clause that specifies
                // merely an exception type
                var hasDeclaration = catchClause.Declaration.Identifier.Kind() != SyntaxKind.None;

                // A variable to store the new unique identifier to store the exception
                IJsDeclaration newIdentifier;

                // Hoist the variable into a field
                if (hasDeclaration)
                {
                    newIdentifier = HoistVariable(new LiftedVariableKey(catchClause.Declaration.Identifier, symbol));
                }
                else
                {
                    newIdentifier = HoistVariable(new LiftedVariableKey(SyntaxFactory.Identifier("ex")));
                }

                // Collect all the catch statements into the catchState by making that state current
                var catchState = GetNextState();
                CurrentState = catchState;
                AcceptStatement(catchClause.Block);

                // Add onto the catch state some commands to go to the next state.
                if (finallyState != null)
                {
                    GotoState(finallyState);
                }
                else
                {
                    GotoState(afterTry);
                }

                // Create the statements that will live in the actual catch handler, which directs the logic
                // to the actual catch state and also stores the exception in the correct identifier.
                var thisCatchStatements = Js.Block();
                thisCatchStatements.Express(newIdentifier.SetReference().Assign(exceptionIdentifier.GetReference()));

                // Apply filter if present
                if (catchClause.Filter != null)
                {
                    var filter = (JsExpression)catchClause.Filter.FilterExpression.Accept(Transformer);
                    thisCatchStatements.Add(Js.If(filter, Js.Block(GotoStateStatements(catchState))));
                }
                else
                {
                    thisCatchStatements.AddRange(GotoStateStatements(catchState));
                }

                // Only do the above if the current exception is of the type expected by the catch handler.
                var condition = Idioms.Is(exceptionIdentifier.GetReference(), exceptionType);
                catchBlock.Add(Js.If(condition, thisCatchStatements));
            }
            if (node.Finally != null)
            {
                // Collect the statements of the finally block into the finally state
                CurrentState = finallyState;
                AcceptStatement(node.Finally.Block);

                // If the exception object is not null, then rethrow it.  In other words, if this is a finally
                // clause that has responded to an exception, we need to propagate the exception rather than
                // continue after the try statement.  Otherwise, go to the code after the try block.
                CurrentState.Add(Js.If(exceptionIdentifier.GetReference().NotEqualTo(Js.Null()),
                                       Js.Throw(exceptionIdentifier.GetReference()),
                                       Js.Block(GotoStateStatements(afterTry))));

                // Finally, at the very end of the catch clause (and we can only get here if the logic didn't break
                // out as it would with the logic in the catch handlers) go to the finally state.
                catchBlock.AddRange(GotoStateStatements(finallyState).ToArray());
            }
            catchBlock.Add(Js.Throw(exceptionIdentifier.GetReference()));

            newTryStatement.Catch      = Js.Catch(Js.Variable(exceptionVariable));
            newTryStatement.Catch.Body = catchBlock;
            tryState.Wrap = switchStatement =>
            {
                newTryStatement.Body = Js.Block(switchStatement);
                return(newTryStatement);
            };

            StartSubstate(tryState);
            AcceptStatement(node.Block);
            if (node.Finally != null)
            {
                GotoState(finallyState);
            }
            else
            {
                GotoState(afterTry);
            }
            EndSubstate();

            CurrentState = afterTry;
        }