protected void AssertCorrect(string orig, string expected, MethodType methodType = MethodType.Normal) { int tempIndex = 0, stateIndex = 0, loopLabelIndex = 0; var stmt = JsBlockStatement.MakeBlock(JavaScriptParser.Parser.ParseStatement(orig)); 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(new JsVariableDeclarationStatement(sm.Variables)); } body.AddRange(sm.FinallyHandlers.Select(h => new JsExpressionStatement(JsExpression.Assign(JsExpression.Identifier(h.Item1), h.Item2)))); if (sm.Disposer != null) { body.Add(new JsExpressionStatement(JsExpression.Assign(JsExpression.Identifier("dispose"), new JsFunctionDefinitionExpression(new string[0], sm.Disposer)))); } body.Add(sm.MainBlock); return(new JsBlockStatement(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 ? new JsVariableDeclaration("$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); }
private bool HandleForStatement(JsForStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock) { if (NeedsBreakBeforeForLoop(currentBlock, stmt, location)) { // We have to create a new block for the statement. var topOfLoopState = CreateNewStateValue(currentState.FinallyStack); Enqueue(stack.Push(new StackEntry(location.Block, location.Index, true)), breakStack, continueStack, topOfLoopState, returnState); if (!(stmt.InitStatement is JsEmptyStatement)) { currentBlock.Add(stmt.InitStatement); } currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState)); return(false); } else { var iteratorState = (stmt.IteratorExpression != null ? CreateNewStateValue(currentState.FinallyStack) : currentState); var afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); if (stmt.ConditionExpression != null) { currentBlock.Add(new JsIfStatement(JsExpression.LogicalNot(stmt.ConditionExpression), new JsGotoStateStatement(afterLoopState.Item1, currentState), null)); } string currentName = GetLabelForState(currentState); currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Body, 0)), breakStack.Push(Tuple.Create(currentName, afterLoopState.Item1)), continueStack.Push(Tuple.Create(currentName, iteratorState)), currentState, iteratorState)); if (stmt.IteratorExpression != null) { Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsBlockStatement.MakeBlock(new JsExpressionStatement(stmt.IteratorExpression)), 0)), breakStack, continueStack, iteratorState, currentState); } if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState); } return(false); } }
internal JsFunctionDefinitionExpression(IEnumerable <string> parameterNames, JsStatement body, string name = null) : base(ExpressionNodeType.FunctionDefinition) { if (parameterNames == null) { throw new ArgumentNullException("parameterNames"); } if (body == null) { throw new ArgumentNullException("body"); } if (name != null && !name.IsValidJavaScriptIdentifier()) { throw new ArgumentException("name"); } ParameterNames = parameterNames.AsReadOnly(); if (ParameterNames.Any(n => !n.IsValidJavaScriptIdentifier())) { throw new ArgumentException("parameterNames"); } Body = JsBlockStatement.MakeBlock(body); Name = name; }
public void GatheringIsCorrect() { var stmt = JsBlockStatement.MakeBlock(JavaScriptParser.Parser.ParseStatement(@" { var a; (function() { var b; c; function d(p1, p2) { e; b; var f; } for (var g = 1;;) { for (var h in x) { } for (i in x) { } } }); try { } catch (ex) { (function() { ex; }); } j; }")); var locals = Minifier.LocalVariableGatherer.Analyze(stmt); var globals = Minifier.ImplicitGlobalsGatherer.Analyze(stmt, locals); var result = new OutputRewriter(locals, globals).Process(stmt); string actual = OutputFormatter.Format(result); Assert.That(actual.Replace("\r\n", "\n"), Is.EqualTo( @"{ locals(a); globals(c, d, e, i, j, x); var a; (function() { locals(b, g, h); globals(c, d, e, i, x); var b; c; function d(p1, p2) { locals(f, p1, p2); globals(e); e; b; var f; } for (var g = 1;;) { for (var h in x) { } for (i in x) { } } }); try { } catch (ex) { (function() { locals(); globals(); ex; }); } j; } ".Replace("\r\n", "\n"))); }
public void MinimizingIdentifiersWorks() { var stmt = JsBlockStatement.MakeBlock(JavaScriptParser.Parser.ParseStatement(@" { var variable1; (function() { var variable2; a; function d(p1, p2) { e; b; p1; variable1; var variable3; } for (var variable4 = 1;;) { for (var variable5 in d) { } for (f in x) { } } }); try { ex; } catch (ex) { (function() { a; ex; }); } j; }")); var result = Minifier.Process(stmt); string actual = OutputFormatter.Format(result); Assert.That(actual.Replace("\r\n", "\n"), Is.EqualTo( @"{ var c; (function() { var g; a; function d(a, d) { e; b; a; c; var f; } for (var h = 1;;) { for (var i in d) { } for (f in x) { } } }); try { ex; } catch (g) { (function() { a; g; }); } j; } ".Replace("\r\n", "\n"))); }