Java tutorial
/* * SonarQube JavaScript Plugin * Copyright (C) 2011-2016 SonarSource SA * mailto:contact AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.javascript.cfg; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.ArrayDeque; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.sonar.javascript.tree.TreeKinds; import org.sonar.plugins.javascript.api.tree.ScriptTree; import org.sonar.plugins.javascript.api.tree.Tree; import org.sonar.plugins.javascript.api.tree.Tree.Kind; import org.sonar.plugins.javascript.api.tree.declaration.InitializedBindingElementTree; import org.sonar.plugins.javascript.api.tree.expression.ArrayLiteralTree; import org.sonar.plugins.javascript.api.tree.expression.AssignmentExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.BinaryExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.CallExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.ComputedPropertyNameTree; import org.sonar.plugins.javascript.api.tree.expression.ConditionalExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.MemberExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.NewExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.ObjectLiteralTree; import org.sonar.plugins.javascript.api.tree.expression.PairPropertyTree; import org.sonar.plugins.javascript.api.tree.expression.ParenthesisedExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.SpreadElementTree; import org.sonar.plugins.javascript.api.tree.expression.TaggedTemplateTree; import org.sonar.plugins.javascript.api.tree.expression.TemplateExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.TemplateLiteralTree; import org.sonar.plugins.javascript.api.tree.expression.UnaryExpressionTree; import org.sonar.plugins.javascript.api.tree.expression.YieldExpressionTree; import org.sonar.plugins.javascript.api.tree.lexical.SyntaxToken; import org.sonar.plugins.javascript.api.tree.statement.BlockTree; import org.sonar.plugins.javascript.api.tree.statement.BreakStatementTree; import org.sonar.plugins.javascript.api.tree.statement.CaseClauseTree; import org.sonar.plugins.javascript.api.tree.statement.ContinueStatementTree; import org.sonar.plugins.javascript.api.tree.statement.DoWhileStatementTree; import org.sonar.plugins.javascript.api.tree.statement.ExpressionStatementTree; import org.sonar.plugins.javascript.api.tree.statement.ForObjectStatementTree; import org.sonar.plugins.javascript.api.tree.statement.ForStatementTree; import org.sonar.plugins.javascript.api.tree.statement.IfStatementTree; import org.sonar.plugins.javascript.api.tree.statement.LabelledStatementTree; import org.sonar.plugins.javascript.api.tree.statement.ReturnStatementTree; import org.sonar.plugins.javascript.api.tree.statement.StatementTree; import org.sonar.plugins.javascript.api.tree.statement.SwitchClauseTree; import org.sonar.plugins.javascript.api.tree.statement.SwitchStatementTree; import org.sonar.plugins.javascript.api.tree.statement.ThrowStatementTree; import org.sonar.plugins.javascript.api.tree.statement.TryStatementTree; import org.sonar.plugins.javascript.api.tree.statement.VariableDeclarationTree; import org.sonar.plugins.javascript.api.tree.statement.VariableStatementTree; import org.sonar.plugins.javascript.api.tree.statement.WhileStatementTree; import org.sonar.plugins.javascript.api.tree.statement.WithStatementTree; /** * Builder of a {@link ControlFlowGraph} for a given {@link ScriptTree} or for the body of a function. * Implementation note: this class starts from the end and goes backward because it's easier to implement. */ class ControlFlowGraphBuilder { private final Set<JsCfgBlock> blocks = new HashSet<>(); private final JsCfgEndBlock end = new JsCfgEndBlock(); private JsCfgBlock currentBlock = createSimpleBlock(end); private JsCfgBlock start; private final Deque<Breakable> breakables = new ArrayDeque<>(); private final Deque<JsCfgBlock> throwTargets = new ArrayDeque<>(); private String currentLabel = null; private static final Kind[] SIMPLE_BINARY_KINDS = { Kind.MULTIPLY, Kind.EXPONENT, Kind.DIVIDE, Kind.REMAINDER, Kind.PLUS, Kind.MINUS, Kind.LEFT_SHIFT, Kind.RIGHT_SHIFT, Kind.UNSIGNED_RIGHT_SHIFT, Kind.RELATIONAL_IN, Kind.INSTANCE_OF, Kind.LESS_THAN, Kind.GREATER_THAN, Kind.LESS_THAN_OR_EQUAL_TO, Kind.GREATER_THAN_OR_EQUAL_TO, Kind.EQUAL_TO, Kind.STRICT_EQUAL_TO, Kind.NOT_EQUAL_TO, Kind.STRICT_NOT_EQUAL_TO, Kind.BITWISE_AND, Kind.BITWISE_XOR, Kind.BITWISE_OR, Kind.COMMA_OPERATOR }; private static final Kind[] UNARY_KINDS = { Kind.POSTFIX_INCREMENT, Kind.POSTFIX_DECREMENT, Kind.PREFIX_INCREMENT, Kind.PREFIX_DECREMENT, Kind.DELETE, Kind.VOID, Kind.TYPEOF, Kind.UNARY_PLUS, Kind.UNARY_MINUS, Kind.BITWISE_COMPLEMENT, Kind.LOGICAL_COMPLEMENT }; ControlFlowGraph createGraph(ScriptTree tree) { List<Tree> items = ImmutableList.of(); if (tree.items() != null) { items = tree.items().items(); } return createGraph(items); } ControlFlowGraph createGraph(BlockTree body) { return createGraph(body.statements()); } private ControlFlowGraph createGraph(List<? extends Tree> items) { throwTargets.push(end); build(items); start = currentBlock; removeEmptyBlocks(); blocks.add(end); return new ControlFlowGraph(blocks, start, end); } private void removeEmptyBlocks() { Map<JsCfgBlock, JsCfgBlock> emptyBlockReplacements = new HashMap<>(); for (JsCfgBlock block : blocks) { if (block.elements().isEmpty()) { JsCfgBlock firstNonEmptySuccessor = block.skipEmptyBlocks(); emptyBlockReplacements.put(block, firstNonEmptySuccessor); for (SyntaxToken jump : block.disconnectingJumps()) { firstNonEmptySuccessor.addDisconnectingJump(jump); } } } blocks.removeAll(emptyBlockReplacements.keySet()); for (JsCfgBlock block : blocks) { block.replaceSuccessors(emptyBlockReplacements); } if (emptyBlockReplacements.containsKey(start)) { start = emptyBlockReplacements.get(start); } } private void build(List<? extends Tree> trees) { for (Tree tree : Lists.reverse(trees)) { build(tree); } } private void build(Tree tree) { if (tree.is(Kind.EXPRESSION_STATEMENT)) { buildExpression(((ExpressionStatementTree) tree).expression()); } else if (tree.is(Kind.VARIABLE_STATEMENT)) { buildExpression(((VariableStatementTree) tree).declaration()); } else if (tree.is(Kind.IF_STATEMENT)) { visitIfStatement((IfStatementTree) tree); } else if (tree.is(Kind.FOR_STATEMENT)) { visitForStatement((ForStatementTree) tree); } else if (tree.is(Kind.FOR_IN_STATEMENT, Kind.FOR_OF_STATEMENT)) { visitForObjectStatement((ForObjectStatementTree) tree); } else if (tree.is(Kind.WHILE_STATEMENT)) { visitWhileStatement((WhileStatementTree) tree); } else if (tree.is(Kind.DO_WHILE_STATEMENT)) { visitDoWhileStatement((DoWhileStatementTree) tree); } else if (tree.is(Kind.CONTINUE_STATEMENT)) { visitContinueStatement((ContinueStatementTree) tree); } else if (tree.is(Kind.BREAK_STATEMENT)) { visitBreakStatement((BreakStatementTree) tree); } else if (tree.is(Kind.RETURN_STATEMENT)) { visitReturnStatement((ReturnStatementTree) tree); } else if (tree.is(Kind.BLOCK)) { visitBlock((BlockTree) tree); } else if (tree.is(Kind.LABELLED_STATEMENT)) { visitLabelledStatement((LabelledStatementTree) tree); } else if (tree.is(Kind.TRY_STATEMENT)) { visitTryStatement((TryStatementTree) tree); } else if (tree.is(Kind.THROW_STATEMENT)) { visitThrowStatement((ThrowStatementTree) tree); } else if (tree.is(Kind.SWITCH_STATEMENT)) { visitSwitchStatement((SwitchStatementTree) tree); } else if (tree.is(Kind.WITH_STATEMENT)) { WithStatementTree with = (WithStatementTree) tree; build(with.statement()); currentBlock.addElement(with.expression()); } else if (tree.is(Kind.DEBUGGER_STATEMENT, Kind.FUNCTION_DECLARATION, Kind.GENERATOR_DECLARATION, Kind.CLASS_DECLARATION, Kind.IMPORT_DECLARATION, Kind.IMPORT_MODULE_DECLARATION, Kind.DEFAULT_EXPORT_DECLARATION, Kind.NAMED_EXPORT_DECLARATION, Kind.NAMESPACE_EXPORT_DECLARATION)) { currentBlock.addElement(tree); } else if (tree.is(Kind.EMPTY_STATEMENT)) { // Nothing to do } else { throw new IllegalArgumentException("Cannot build CFG for " + tree); } } private void buildExpressions(List<? extends Tree> expressions) { for (Tree expression : Lists.reverse(expressions)) { buildExpression(expression); } } private void buildExpression(Tree tree) { if (!tree.is(Kind.CONDITIONAL_OR, Kind.CONDITIONAL_AND)) { currentBlock.addElement(tree); } if (tree.is(SIMPLE_BINARY_KINDS)) { BinaryExpressionTree binary = (BinaryExpressionTree) tree; buildExpression(binary.rightOperand()); buildExpression(binary.leftOperand()); } else if (TreeKinds.isAssignment(tree)) { AssignmentExpressionTree assignment = (AssignmentExpressionTree) tree; buildExpression(assignment.variable()); buildExpression(assignment.expression()); } else if (tree.is(UNARY_KINDS)) { UnaryExpressionTree unary = (UnaryExpressionTree) tree; buildExpression(unary.expression()); } else if (tree.is(Kind.ARRAY_LITERAL)) { ArrayLiteralTree arrayLiteral = (ArrayLiteralTree) tree; buildExpressions(arrayLiteral.elements()); } else if (tree.is(Kind.OBJECT_LITERAL)) { ObjectLiteralTree objectLiteral = (ObjectLiteralTree) tree; buildExpressions(objectLiteral.properties()); } else if (tree.is(Kind.DOT_MEMBER_EXPRESSION)) { MemberExpressionTree memberExpression = (MemberExpressionTree) tree; buildExpression(memberExpression.object()); } else if (tree.is(Kind.BRACKET_MEMBER_EXPRESSION)) { MemberExpressionTree memberExpression = (MemberExpressionTree) tree; buildExpression(memberExpression.property()); buildExpression(memberExpression.object()); } else if (tree.is(Kind.CALL_EXPRESSION)) { CallExpressionTree callExpression = (CallExpressionTree) tree; buildExpressions(callExpression.arguments().parameters()); buildExpression(callExpression.callee()); } else if (tree.is(Kind.VAR_DECLARATION, Kind.LET_DECLARATION, Kind.CONST_DECLARATION)) { VariableDeclarationTree declaration = (VariableDeclarationTree) tree; buildExpressions(declaration.variables()); } else if (tree.is(Kind.INITIALIZED_BINDING_ELEMENT)) { InitializedBindingElementTree initializedBindingElementTree = (InitializedBindingElementTree) tree; buildExpression(initializedBindingElementTree.left()); buildExpression(initializedBindingElementTree.right()); } else if (tree.is(Kind.PAIR_PROPERTY)) { PairPropertyTree pairProperty = (PairPropertyTree) tree; buildExpression(pairProperty.value()); buildExpression(pairProperty.key()); } else if (tree.is(Kind.COMPUTED_PROPERTY_NAME)) { ComputedPropertyNameTree computedPropertyName = (ComputedPropertyNameTree) tree; buildExpression(computedPropertyName.expression()); } else if (tree.is(Kind.NEW_EXPRESSION)) { NewExpressionTree newExpression = (NewExpressionTree) tree; if (newExpression.arguments() != null) { buildExpressions(newExpression.arguments().parameters()); } buildExpression(newExpression.expression()); } else if (tree.is(Kind.CONDITIONAL_AND)) { BinaryExpressionTree binary = (BinaryExpressionTree) tree; JsCfgBlock falseSuccessor = (JsCfgBlock) ControlFlowGraph.falseSuccessorFor(currentBlock); if (!currentBlock.elements().isEmpty()) { falseSuccessor = currentBlock; currentBlock = createSimpleBlock(currentBlock); } buildExpression(binary.rightOperand()); currentBlock = createBranchingBlock(tree, currentBlock, falseSuccessor); buildExpression(binary.leftOperand()); } else if (tree.is(Kind.CONDITIONAL_OR)) { BinaryExpressionTree binary = (BinaryExpressionTree) tree; JsCfgBlock trueSuccessor = (JsCfgBlock) ControlFlowGraph.trueSuccessorFor(currentBlock); if (!currentBlock.elements().isEmpty()) { trueSuccessor = currentBlock; currentBlock = createSimpleBlock(currentBlock); } buildExpression(binary.rightOperand()); currentBlock = createBranchingBlock(tree, trueSuccessor, currentBlock); buildExpression(binary.leftOperand()); } else if (tree.is(Kind.CONDITIONAL_EXPRESSION)) { ConditionalExpressionTree conditionalExpression = (ConditionalExpressionTree) tree; JsCfgBlock successor = currentBlock; currentBlock = createSimpleBlock(successor); buildExpression(conditionalExpression.falseExpression()); JsCfgBlock falseBlock = currentBlock; currentBlock = createSimpleBlock(successor); buildExpression(conditionalExpression.trueExpression()); JsCfgBlock trueBlock = currentBlock; currentBlock = createBranchingBlock(tree, trueBlock, falseBlock); buildExpression(conditionalExpression.condition()); } else if (tree.is(Kind.SPREAD_ELEMENT)) { SpreadElementTree spreadElement = (SpreadElementTree) tree; buildExpression(spreadElement.element()); } else if (tree.is(Kind.PARENTHESISED_EXPRESSION)) { ParenthesisedExpressionTree parenthesisedExpression = (ParenthesisedExpressionTree) tree; buildExpression(parenthesisedExpression.expression()); } else if (tree.is(Kind.TEMPLATE_LITERAL)) { TemplateLiteralTree templateLiteral = (TemplateLiteralTree) tree; buildExpressions(templateLiteral.expressions()); } else if (tree.is(Kind.TEMPLATE_EXPRESSION)) { TemplateExpressionTree templateExpression = (TemplateExpressionTree) tree; buildExpression(templateExpression.expression()); } else if (tree.is(Kind.TAGGED_TEMPLATE)) { TaggedTemplateTree taggedTemplate = (TaggedTemplateTree) tree; buildExpression(taggedTemplate.template()); buildExpression(taggedTemplate.callee()); } else if (tree.is(Kind.YIELD_EXPRESSION)) { YieldExpressionTree yieldExpression = (YieldExpressionTree) tree; buildExpression(yieldExpression.argument()); } } private void visitBlock(BlockTree block) { build(block.statements()); } private void addBreakable(JsCfgBlock breakTarget, JsCfgBlock continueTarget, String label) { breakables.addFirst(new Breakable(continueTarget, breakTarget, label)); } private void removeBreakable() { breakables.removeFirst(); } private void visitReturnStatement(ReturnStatementTree tree) { currentBlock.addDisconnectingJump(tree.returnKeyword()); currentBlock = createSimpleBlock(tree, end); if (tree.expression() != null) { buildExpression(tree.expression()); } } private void visitContinueStatement(ContinueStatementTree tree) { JsCfgBlock target = null; String label = tree.label() == null ? null : tree.label().name(); for (Breakable breakable : breakables) { if (breakable.continueTarget != null && (label == null || label.equals(breakable.label))) { target = breakable.continueTarget; break; } } Preconditions.checkState(target != null, "No continue target can be found for label " + label); currentBlock.addDisconnectingJump(tree.continueKeyword()); currentBlock = createSimpleBlock(tree, target); } private void visitBreakStatement(BreakStatementTree tree) { JsCfgBlock target = null; String label = tree.label() == null ? null : tree.label().name(); for (Breakable breakable : breakables) { if (label == null || label.equals(breakable.label)) { target = breakable.breakTarget; break; } } Preconditions.checkState(target != null, "No break target can be found for label " + label); currentBlock.addDisconnectingJump(tree.breakKeyword()); currentBlock = createSimpleBlock(tree, target); } private void visitIfStatement(IfStatementTree tree) { JsCfgBlock successor = currentBlock; if (tree.elseClause() != null) { buildSubFlow(tree.elseClause().statement(), successor); } JsCfgBlock elseBlock = currentBlock; buildSubFlow(tree.statement(), successor); JsCfgBlock thenBlock = currentBlock; JsCfgBranchingBlock branchingBlock = createBranchingBlock(tree, thenBlock, elseBlock); currentBlock = branchingBlock; buildExpression(tree.condition()); } private void visitForStatement(ForStatementTree tree) { JsCfgBlock forStatementSuccessor = currentBlock; JsCfgForwardingBlock linkToCondition = createForwardingBlock(); JsCfgForwardingBlock linkToUpdate = createForwardingBlock(); JsCfgBlock loopBodyBlock = buildLoopBody(tree.statement(), linkToUpdate, currentBlock); if (tree.update() != null) { currentBlock = createSimpleBlock(linkToCondition); buildExpression(tree.update()); linkToUpdate.setSuccessor(currentBlock); } else { linkToUpdate.setSuccessor(linkToCondition); } if (tree.condition() != null) { currentBlock = createBranchingBlock(tree, loopBodyBlock, forStatementSuccessor); buildExpression(tree.condition()); linkToCondition.setSuccessor(currentBlock); } else { linkToCondition.setSuccessor(loopBodyBlock); } currentBlock = createSimpleBlock(linkToCondition); if (tree.init() != null) { buildExpression(tree.init()); } else if (tree.condition() == null && loopBodyBlock.elements().isEmpty()) { loopBodyBlock.addElement(tree.forKeyword()); } } private void visitForObjectStatement(ForObjectStatementTree tree) { JsCfgBlock successor = currentBlock; JsCfgForwardingBlock linkToAssignment = createForwardingBlock(); JsCfgBlock loopBodyBlock = buildLoopBody(tree.statement(), linkToAssignment, currentBlock); JsCfgBranchingBlock assignmentBlock = createBranchingBlock(tree, loopBodyBlock, successor); currentBlock = assignmentBlock; buildExpression(tree.variableOrExpression()); buildExpression(tree.expression()); linkToAssignment.setSuccessor(currentBlock); currentBlock = createSimpleBlock(currentBlock); } private void visitWhileStatement(WhileStatementTree tree) { JsCfgBlock successor = currentBlock; JsCfgForwardingBlock linkToCondition = createForwardingBlock(); JsCfgBlock loopBodyBlock = buildLoopBody(tree.statement(), linkToCondition, successor); currentBlock = createBranchingBlock(tree, loopBodyBlock, successor); buildExpression(tree.condition()); linkToCondition.setSuccessor(currentBlock); currentBlock = createSimpleBlock(currentBlock); } private void visitDoWhileStatement(DoWhileStatementTree tree) { JsCfgBlock successor = currentBlock; JsCfgForwardingBlock linkToBody = createForwardingBlock(); currentBlock = createBranchingBlock(tree, linkToBody, successor); buildExpression(tree.condition()); JsCfgBlock loopBodyBlock = buildLoopBody(tree.statement(), currentBlock, successor); linkToBody.setSuccessor(loopBodyBlock); currentBlock = createSimpleBlock(loopBodyBlock); } private void visitLabelledStatement(LabelledStatementTree tree) { String label = tree.label().name(); boolean isLoopStatement = tree.statement().is(Kind.FOR_STATEMENT, Kind.FOR_IN_STATEMENT, Kind.FOR_OF_STATEMENT, Kind.WHILE_STATEMENT, Kind.DO_WHILE_STATEMENT); if (!isLoopStatement) { addBreakable(currentBlock, null, label); currentBlock = createSimpleBlock(currentBlock); } else { // Loop statements manage breakables themselves to set the target for "continue" currentLabel = label; } build(tree.statement()); if (!isLoopStatement) { removeBreakable(); } } private void visitTryStatement(TryStatementTree tree) { JsCfgBlock catchOrFinallyBlock = null; if (tree.finallyBlock() != null) { currentBlock = createSimpleBlock(currentBlock); build(tree.finallyBlock()); throwTargets.push(currentBlock); catchOrFinallyBlock = currentBlock; } if (tree.catchBlock() != null) { JsCfgBlock catchSuccessor = currentBlock; buildSubFlow(tree.catchBlock().block(), currentBlock); JsCfgBranchingBlock catchBlock = createBranchingBlock(tree.catchBlock(), currentBlock, catchSuccessor); currentBlock = catchBlock; buildExpression(tree.catchBlock().parameter()); catchOrFinallyBlock = catchBlock; currentBlock = catchBlock; } if (tree.finallyBlock() != null) { throwTargets.pop(); } throwTargets.push(currentBlock); currentBlock = createSimpleBlock(currentBlock); build(tree.block()); throwTargets.pop(); currentBlock = createBranchingBlock(tree, currentBlock, catchOrFinallyBlock); currentBlock.addElement(tree.tryKeyword()); } private void visitThrowStatement(ThrowStatementTree tree) { currentBlock.addDisconnectingJump(tree.throwKeyword()); currentBlock = createSimpleBlock(throwTargets.peek()); buildExpression(tree.expression()); } private void visitSwitchStatement(SwitchStatementTree tree) { addBreakable(currentBlock, null, null); JsCfgBlock nextStatementBlock = currentBlock; JsCfgForwardingBlock defaultForwardingBlock = createForwardingBlock(); defaultForwardingBlock.setSuccessor(currentBlock); JsCfgBlock nextCase = defaultForwardingBlock; for (SwitchClauseTree switchCaseClause : Lists.reverse(tree.cases())) { if (switchCaseClause.is(Kind.CASE_CLAUSE)) { currentBlock = createSimpleBlock(nextStatementBlock); build(switchCaseClause.statements()); if (!switchCaseClause.statements().isEmpty()) { nextStatementBlock = currentBlock; } CaseClauseTree caseClause = (CaseClauseTree) switchCaseClause; currentBlock = createBranchingBlock(caseClause, nextStatementBlock, nextCase); buildExpression(caseClause.expression()); nextCase = currentBlock; } else { currentBlock = createSimpleBlock(nextStatementBlock); build(switchCaseClause.statements()); defaultForwardingBlock.setSuccessor(currentBlock); if (!switchCaseClause.statements().isEmpty()) { nextStatementBlock = currentBlock; } } } removeBreakable(); currentBlock = createSimpleBlock(nextCase); buildExpression(tree.expression()); } private JsCfgBlock buildLoopBody(StatementTree body, JsCfgBlock conditionBlock, JsCfgBlock breakTarget) { addBreakable(breakTarget, conditionBlock, currentLabel); currentLabel = null; buildSubFlow(body, conditionBlock); JsCfgBlock loopBodyBlock = currentBlock; removeBreakable(); return loopBodyBlock; } private void buildSubFlow(StatementTree subFlowTree, JsCfgBlock successor) { currentBlock = createSimpleBlock(successor); build(subFlowTree); } private JsCfgBranchingBlock createBranchingBlock(Tree branchingTree, JsCfgBlock trueSuccessor, JsCfgBlock falseSuccessor) { JsCfgBranchingBlock block = new JsCfgBranchingBlock(branchingTree, trueSuccessor, falseSuccessor); blocks.add(block); return block; } private JsCfgBlock createSimpleBlock(Tree element, JsCfgBlock successor) { JsCfgBlock block = createSimpleBlock(successor); block.addElement(element); return block; } private JsCfgBlock createSimpleBlock(JsCfgBlock successor) { JsCfgBlock block = new JsCfgBlock(successor); blocks.add(block); return block; } private JsCfgForwardingBlock createForwardingBlock() { JsCfgForwardingBlock block = new JsCfgForwardingBlock(); blocks.add(block); return block; } private static class Breakable { final JsCfgBlock continueTarget; final JsCfgBlock breakTarget; final String label; Breakable(JsCfgBlock continueTarget, JsCfgBlock breakTarget, String label) { this.continueTarget = continueTarget; this.breakTarget = breakTarget; this.label = label; } } }