public void VariableDeclarationStatementsAreCorrectlyOutput() { AssertCorrect(JsStatement.Var(new[] { JsStatement.Declaration("i", null) }), "var i;\n"); AssertCorrect(JsStatement.Var(new[] { JsStatement.Declaration("i", null), JsStatement.Declaration("j", null) }), "var i, j;\n"); AssertCorrect(JsStatement.Var(new[] { JsStatement.Declaration("i", JsExpression.Number(0)) }), "var i = 0;\n"); AssertCorrect(JsStatement.Var(new[] { JsStatement.Declaration("i", JsExpression.Number(0)), JsStatement.Declaration("j", JsExpression.Number(1)) }), "var i = 0, j = 1;\n"); AssertCorrect(JsStatement.Var(new[] { JsStatement.Declaration("i", JsExpression.Number(0)), JsStatement.Declaration("j", null) }), "var i = 0, j;\n"); }
internal JsBlockStatement Process(JsBlockStatement statement) { _allocateFinallyHandler = null; _makeSetCurrent = null; var result = Process(statement, false, false); var hoistResult = VariableHoistingVisitor.Process(result); return(JsStatement.Block(new[] { JsStatement.Var(new[] { JsStatement.Declaration(_stateVariableName, JsExpression.Number(0)) }.Concat(hoistResult.Item2.Select(v => JsStatement.Declaration(v, null)))) }.Concat(hoistResult.Item1.Statements))); }
private JsBlockStatement ProcessAsyncMethod(JsBlockStatement statement, string stateMachineMethodName, string doFinallyBlocksVariableName, JsVariableDeclaration taskCompletionSource, Func <JsExpression, JsExpression> makeSetResult, Func <JsExpression, JsExpression> makeSetException, Func <JsExpression> getTask, Func <JsExpression, JsExpression, JsExpression> bindToContext) { _stateMachineMethodName = stateMachineMethodName; _doFinallyBlocksVariableName = doFinallyBlocksVariableName; _makeSetResult = taskCompletionSource != null ? makeSetResult : null; _needDoFinallyBlocksVariable = new HasAwaitInsideTryWithFinallyVisitor().Analyze(statement); var result = Process(statement, isIteratorBlock: false, isAsync: true); var hoistResult = VariableHoistingVisitor.Process(result); string catchVariable = _allocateTempVariable(); JsStatement body; if (taskCompletionSource != null && (statement.Statements.Count == 0 || IsNextStatementReachable(statement.Statements[statement.Statements.Count - 1]))) // If we return the task, and if we risk falling out of the original method, we need to add a setResult call. { body = JsStatement.Block(hoistResult.Item1.Statements.Concat(new JsStatement[] { makeSetResult(null) })); } else { body = hoistResult.Item1; } if (taskCompletionSource != null) { body = JsStatement.Try(body, JsStatement.Catch(catchVariable, JsStatement.Block(makeSetException(JsExpression.Identifier(catchVariable)))), null); } IEnumerable <JsVariableDeclaration> declarations = new[] { JsStatement.Declaration(_stateVariableName, JsExpression.Number(0)) }; if (taskCompletionSource != null) { declarations = declarations.Concat(new[] { taskCompletionSource }); } declarations = declarations.Concat(hoistResult.Item2.Select(v => JsStatement.Declaration(v, null))); if (_needDoFinallyBlocksVariable) { body = JsStatement.Block(new[] { JsStatement.Var(_doFinallyBlocksVariableName, JsExpression.True) }.Concat(body is JsBlockStatement ? ((JsBlockStatement)body).Statements : (IEnumerable <JsStatement>) new[] { body })); } var stateMachine = JsExpression.FunctionDefinition(new string[0], body); var boundStateMachine = UsesThisVisitor.Analyze(stateMachine.Body) ? bindToContext(stateMachine, JsExpression.This) : stateMachine; IEnumerable <JsStatement> stmts = new JsStatement[] { JsStatement.Var(declarations), JsStatement.Var(stateMachineMethodName, boundStateMachine), JsExpression.Invocation(JsExpression.Identifier(stateMachineMethodName)) }; if (taskCompletionSource != null) { stmts = stmts.Concat(new[] { JsStatement.Return(getTask()) }); } return(JsStatement.Block(stmts)); }
public override JsVariableDeclaration VisitVariableDeclaration(JsVariableDeclaration declaration, Tuple <Dictionary <string, string>, HashSet <string> > data) { string newName; if (data.Item1.TryGetValue(declaration.Name, out newName)) { return(JsStatement.Declaration(newName, declaration.Initializer != null ? VisitExpression(declaration.Initializer, data) : null)); } else { return(base.VisitVariableDeclaration(declaration, data)); } }
private IteratorStateMachine ProcessIteratorBlock(JsBlockStatement statement, Func <string> allocateFinallyHandler, Func <JsExpression, JsExpression> makeSetCurrent) { _allocateFinallyHandler = allocateFinallyHandler; _makeSetCurrent = makeSetCurrent; var result = Process(statement, isIteratorBlock: true, isAsync: false); var stateFinallyHandlers = _allStates.Where(s => !s.FinallyStack.IsEmpty).Select(s => Tuple.Create(s.StateValue, s.FinallyStack.Select(x => x.Item2).Reverse().ToList())).ToList(); var hoistResult = VariableHoistingVisitor.Process(result); return(new IteratorStateMachine(hoistResult.Item1, new[] { JsStatement.Declaration(_stateVariableName, JsExpression.Number(0)) }.Concat(hoistResult.Item2.Select(v => JsStatement.Declaration(v, null))), _finallyHandlers.Select(h => Tuple.Create(h.Item1, JsExpression.FunctionDefinition(new string[0], h.Item2))), stateFinallyHandlers.Count > 0 ? DisposeGenerator.GenerateDisposer(_stateVariableName, stateFinallyHandlers) : null)); }
public void ForStatementsAreCorrectlyOutput() { AssertCorrect(JsStatement.For(JsStatement.Var("i", JsExpression.Number(0)), JsExpression.Lesser(JsExpression.Identifier("i"), JsExpression.Number(10)), JsExpression.PostfixPlusPlus(JsExpression.Identifier("i")), JsStatement.EmptyBlock), "for(var i=0;i<10;i++){}"); AssertCorrect(JsStatement.For(JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), JsExpression.Lesser(JsExpression.Identifier("i"), JsExpression.Number(10)), JsExpression.PostfixPlusPlus(JsExpression.Identifier("i")), JsStatement.EmptyBlock), "for(i=0;i<10;i++){}"); AssertCorrect(JsStatement.For(JsStatement.Var(JsStatement.Declaration("i", JsExpression.Number(0)), JsStatement.Declaration("j", JsExpression.Number(1))), JsExpression.Lesser(JsExpression.Identifier("i"), JsExpression.Number(10)), JsExpression.Comma(JsExpression.PostfixPlusPlus(JsExpression.Identifier("i")), JsExpression.PostfixPlusPlus(JsExpression.Identifier("j"))), JsStatement.EmptyBlock), "for(var i=0,j=1;i<10;i++,j++){}"); AssertCorrect(JsStatement.For(JsStatement.Empty, null, null, JsStatement.EmptyBlock), "for(;;){}"); }
protected void AssertCorrect(string orig, string expected, MethodType methodType = MethodType.Normal) { int tempIndex = 0, stateIndex = 0, loopLabelIndex = 0; var stmt = JsStatement.EnsureBlock(JavaScriptParser.Parser.ParseStatement(orig, allowCustomKeywords: true)); JsBlockStatement result; if (methodType == MethodType.Iterator) { int finallyHandlerIndex = 0; result = StateMachineRewriter.RewriteIteratorBlock(stmt, e => e.NodeType != ExpressionNodeType.Identifier, () => "$tmp" + (++tempIndex).ToString(CultureInfo.InvariantCulture), () => "$state" + (++stateIndex).ToString(CultureInfo.InvariantCulture), () => string.Format("$loop" + (++loopLabelIndex).ToString(CultureInfo.InvariantCulture)), () => string.Format("$finally" + (++finallyHandlerIndex).ToString(CultureInfo.InvariantCulture)), v => JsExpression.Invocation(JsExpression.Identifier("setCurrent"), v), sm => { var body = new List <JsStatement>(); if (sm.Variables.Count > 0) { body.Add(JsStatement.Var(sm.Variables)); } body.AddRange(sm.FinallyHandlers.Select(h => (JsStatement)JsExpression.Assign(JsExpression.Identifier(h.Item1), h.Item2))); if (sm.Disposer != null) { body.Add(JsExpression.Assign(JsExpression.Identifier("dispose"), JsExpression.FunctionDefinition(new string[0], sm.Disposer))); } body.Add(sm.MainBlock); return(JsStatement.Block(body)); }); } else if (methodType == MethodType.AsyncTask || methodType == MethodType.AsyncVoid) { result = StateMachineRewriter.RewriteAsyncMethod(stmt, e => e.NodeType != ExpressionNodeType.Identifier, () => "$tmp" + (++tempIndex).ToString(CultureInfo.InvariantCulture), () => "$state" + (++stateIndex).ToString(CultureInfo.InvariantCulture), () => string.Format("$loop" + (++loopLabelIndex).ToString(CultureInfo.InvariantCulture)), "$sm", "$doFinally", methodType == MethodType.AsyncTask ? JsStatement.Declaration("$tcs", JsExpression.New(JsExpression.Identifier("TaskCompletionSource"))) : null, expr => { if (methodType != MethodType.AsyncTask) { throw new InvalidOperationException("Should not set result in async void method"); } return(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("$tcs"), "setResult"), expr ?? JsExpression.String("<<null>>"))); }, expr => { if (methodType != MethodType.AsyncTask) { throw new InvalidOperationException("Should not set exception in async void method"); } return(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("$tcs"), "setException"), expr)); }, () => { if (methodType != MethodType.AsyncTask) { throw new InvalidOperationException("Should not get task async void method"); } return(JsExpression.Invocation(JsExpression.Member(JsExpression.Identifier("$tcs"), "getTask"))); }, (sm, ctx) => JsExpression.Invocation(JsExpression.Identifier("$Bind"), sm, ctx)); } else { result = StateMachineRewriter.RewriteNormalMethod(stmt, e => e.NodeType != ExpressionNodeType.Identifier, () => "$tmp" + (++tempIndex).ToString(CultureInfo.InvariantCulture), () => "$state" + (++stateIndex).ToString(CultureInfo.InvariantCulture), () => string.Format("$loop" + (++loopLabelIndex).ToString(CultureInfo.InvariantCulture))); } var actual = OutputFormatter.Format(result); Assert.That(actual.Replace("\r\n", "\n"), Is.EqualTo(expected.Replace("\r\n", "\n")), "Expected:\n" + expected + "\n\nActual:\n" + actual); }
public virtual JsVariableDeclaration VisitVariableDeclaration(JsVariableDeclaration declaration, TData data) { var after = (declaration.Initializer != null ? VisitExpression(declaration.Initializer, data) : null); return(ReferenceEquals(after, declaration.Initializer) ? declaration : JsStatement.Declaration(declaration.Name, after)); }