private JsExpression GenerateStructEqualsMethod(ITypeDefinition type, string typeVariableName) { var o = JsExpression.Identifier("o"); var parts = new List <JsExpression>(); foreach (var f in type.Fields.Where(f => !f.IsStatic)) { var expr = GenerateFieldCompare(f, o); if (expr != null) { parts.Add(expr); } } JsExpression typeCompare = JsExpression.Invocation(JsExpression.Member(_systemScript, "isInstanceOfType"), o, JsExpression.Identifier(typeVariableName)); if (parts.Count == 0) { return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Return(typeCompare))); } else { return(JsExpression.FunctionDefinition(new[] { "o" }, JsStatement.Block( JsStatement.If(JsExpression.LogicalNot(typeCompare), JsStatement.Return(JsExpression.False), null ), JsStatement.Return(parts.Aggregate((old, p) => old == null ? p : JsExpression.LogicalAnd(old, p))) ))); } }
private bool HandleWhileStatement(JsWhileStatement 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 (!isFirstStatement) { // We have to create a new block for the statement. var topOfLoopState = CreateNewStateValue(currentState.FinallyStack); Enqueue(stack.Push(location), breakStack, continueStack, topOfLoopState, returnState); currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState)); return(false); } else { var afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); currentBlock.Add(JsStatement.If(JsExpression.LogicalNot(stmt.Condition), new JsGotoStateStatement(afterLoopState.Item1, currentState), null)); var 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, currentState)), currentState, currentState, false, false)); if (!stack.IsEmpty || location.Index < location.Block.Statements.Count - 1) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState); } return(false); } }
public void IfStatementIsCorrectlyOutput() { AssertCorrect(JsStatement.If(JsExpression.True, JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), null), "if (true) {\n\ti = 0;\n}\n"); AssertCorrect(JsStatement.If(JsExpression.True, JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(1))), "if (true) {\n\ti = 0;\n}\nelse {\n\ti = 1;\n}\n"); }
public void IfAndElseIfStatementsAreChained() { AssertCorrect(JsStatement.If(JsExpression.True, JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), null), "if(true){i=0;}"); AssertCorrect(JsStatement.If(JsExpression.Identifier("a"), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(0)), JsStatement.If(JsExpression.Identifier("b"), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(1)), JsStatement.If(JsExpression.Identifier("c"), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(2)), JsExpression.Assign(JsExpression.Identifier("i"), JsExpression.Number(3))))), "if(a){i=0;}else if(b){i=1;}else if(c){i=2;}else{i=3;}"); }
private bool HandleIfStatement(JsIfStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock) { var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); IList <JsStatement> thenPart, elsePart; if (stmt.Then.Statements.Count == 0) { thenPart = EmptyList <JsStatement> .Instance; } else { thenPart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Then, 0)), breakStack, continueStack, currentState, stateAfter.Item1, false, false); } if (stmt.Else == null) { elsePart = null; } else if (stmt.Else.Statements.Count == 0) { elsePart = EmptyList <JsStatement> .Instance; } else { elsePart = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Else, 0)), breakStack, continueStack, currentState, stateAfter.Item1, false, false); } currentBlock.Add(JsStatement.If(stmt.Test, JsStatement.Block(thenPart), elsePart != null ? JsStatement.Block(elsePart) : null)); if (thenPart.Count == 0 || elsePart == null || elsePart.Count == 0) { currentBlock.Add(new JsGotoStateStatement(stateAfter.Item1, currentState)); } if (stateAfter.Item2) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState); return(false); } return(true); }
private bool HandleDoWhileStatement(JsDoWhileStatement 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 (!isFirstStatement) { // We have to create a new block for the statement. var topOfLoopState = CreateNewStateValue(currentState.FinallyStack); Enqueue(stack.Push(location), breakStack, continueStack, topOfLoopState, returnState); currentBlock.Add(new JsGotoStateStatement(topOfLoopState, currentState)); return(false); } else { var beforeConditionState = CreateNewStateValue(currentState.FinallyStack); Tuple <State, bool> afterLoopState; string currentName = GetLabelForState(currentState); if (new ContainsBreakVisitor().Analyze(stmt.Body, currentName)) { afterLoopState = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); breakStack = breakStack.Push(Tuple.Create(currentName, afterLoopState.Item1)); } else { afterLoopState = Tuple.Create(returnState, false); } currentBlock.AddRange(Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(stmt.Body, 0)), breakStack, continueStack.Push(Tuple.Create(GetLabelForState(currentState), beforeConditionState)), currentState, beforeConditionState, false, false)); if (afterLoopState.Item2) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, afterLoopState.Item1, returnState); Enqueue(stack.Push(new StackEntry(JsStatement.Block(JsStatement.If(stmt.Condition, new JsGotoStateStatement(currentState, currentState), null)), 0)), breakStack, continueStack, beforeConditionState, afterLoopState.Item1); } else { Enqueue(PushFollowing(stack, location).Push(new StackEntry(JsStatement.Block(JsStatement.If(stmt.Condition, new JsGotoStateStatement(currentState, currentState), null)), 0)), breakStack, continueStack, beforeConditionState, returnState); } return(false); } }
public void CanAnalyzeStatements() { var asm = Common.CreateMockAssembly(); var t1 = Common.CreateMockTypeDefinition("Type", asm); var t2 = Common.CreateMockTypeDefinition("Type", asm); var t3 = Common.CreateMockTypeDefinition("Type", asm); var ast = new JsStatement[] { JsStatement.If(JsExpression.Member(new JsTypeReferenceExpression(t1), "X"), JsStatement.Block( JsExpression.Add(new JsTypeReferenceExpression(t2), new JsTypeReferenceExpression(t3)) ), null), JsExpression.Add(JsExpression.Number(1), new JsTypeReferenceExpression(t1)), }; var refs = TypeReferenceFinder.Analyze(ast); Assert.That(refs, Has.Count.EqualTo(3)); Assert.That(refs.Contains(t1)); Assert.That(refs.Contains(t2)); Assert.That(refs.Contains(t3)); }
private bool HandleSwitchStatement(JsSwitchStatement stmt, StackEntry location, ImmutableStack <StackEntry> stack, ImmutableStack <Tuple <string, State> > breakStack, ImmutableStack <Tuple <string, State> > continueStack, State currentState, State returnState, IList <JsStatement> currentBlock) { var stateAfter = GetStateAfterStatement(location, stack, currentState.FinallyStack, returnState); JsExpression expression = stmt.Expression; if (_isExpressionComplexEnoughForATemporaryVariable(expression)) { string newName = _allocateTempVariable(); currentBlock.Add(JsStatement.Var(newName, expression)); expression = JsExpression.Identifier(newName); } var clauses = new List <Tuple <JsExpression, JsBlockStatement> >(); JsStatement defaultClause = null; State? currentFallthroughState = null; for (int i = 0; i < stmt.Sections.Count; i++) { var clause = stmt.Sections[i]; var origBody = new List <JsStatement>(); origBody.AddRange(clause.Body.Statements); State?nextFallthroughState; if (i < stmt.Sections.Count - 1 && (origBody.Count == 0 || IsNextStatementReachable(origBody[origBody.Count - 1]))) { // Fallthrough var nextBody = stmt.Sections[i + 1].Body.Statements; if (nextBody.Count > 0 && nextBody[0] is JsLabelledStatement) { nextFallthroughState = GetOrCreateStateForLabel(((JsLabelledStatement)nextBody[0]).Label, currentState.FinallyStack); } else { nextFallthroughState = CreateNewStateValue(currentState.FinallyStack); } } else { nextFallthroughState = null; } breakStack = breakStack.Push(Tuple.Create(GetLabelForState(currentState), stateAfter.Item1)); IList <JsStatement> body; if (currentFallthroughState != null) { body = new List <JsStatement>(); body.Add(new JsGotoStateStatement(currentFallthroughState.Value, currentState)); Enqueue(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(origBody), 0)), breakStack, continueStack, currentFallthroughState.Value, stateAfter.Item1); } else { body = Handle(ImmutableStack <StackEntry> .Empty.Push(new StackEntry(JsStatement.Block(origBody), 0)), breakStack, continueStack, currentState, nextFallthroughState ?? stateAfter.Item1, false, false); } if (clause.Values.Any(v => v == null)) { defaultClause = JsStatement.Block(body); } else { JsExpression test = clause.Values.Select(v => JsExpression.Same(expression, v)).Aggregate((o, e) => o != null ? JsExpression.LogicalOr(o, e) : e); clauses.Add(Tuple.Create(test, JsStatement.Block(body))); } currentFallthroughState = nextFallthroughState; } clauses.Reverse(); currentBlock.Add(clauses.Where(c => c.Item1 != null).Aggregate(defaultClause, (o, n) => JsStatement.If(n.Item1, n.Item2, o))); currentBlock.Add(new JsGotoStateStatement(stateAfter.Item1, currentState)); if (stateAfter.Item2) { Enqueue(PushFollowing(stack, location), breakStack, continueStack, stateAfter.Item1, returnState); return(false); } 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, 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 = JsStatement.Block(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(JsStatement.Block(JsStatement.BlockMerged(new JsStatement[0])), 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(JsStatement.Try(JsStatement.Try(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 = JsStatement.Block(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 = JsStatement.Catch(stmt.Catch.Identifier, JsStatement.Block(new[] { new JsSetNextStateStatement(inner.Item2) }.Concat(inner.Item1))); } else { var body = rewriter.Process(stmt.Catch.Body); @catch = ReferenceEquals(body, stmt.Catch.Body) ? stmt.Catch : JsStatement.Catch(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 = JsStatement.Block(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 = JsStatement.Block(JsStatement.If(JsExpression.Identifier(_doFinallyBlocksVariableName), @finally, null)); } } else { @finally = null; } if (currentBlock.Count > 0 && _childStates.ContainsKey(currentState.StateValue)) { var newBlock = JsStatement.If(JsExpression.Same(JsExpression.Identifier(_stateVariableName), JsExpression.Number(currentState.StateValue)), JsStatement.Block(currentBlock), null); currentBlock.Clear(); currentBlock.Add(newBlock); } currentBlock.Add(JsStatement.Try(guarded, @catch, @finally)); return(true); } }
public virtual JsStatement VisitIfStatement(JsIfStatement statement, TData data) { var test = VisitExpression(statement.Test, data); var then = VisitStatement(statement.Then, data); var @else = statement.Else != null?VisitStatement(statement.Else, data) : null; return(ReferenceEquals(test, statement.Test) && ReferenceEquals(then, statement.Then) && ReferenceEquals(@else, statement.Else) ? statement : JsStatement.If(test, then, @else)); }