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; }
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; }