mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
667 lines
18 KiB
C++
667 lines
18 KiB
C++
/*
|
|
This file is part of solidity.
|
|
|
|
solidity is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
solidity 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
|
#include <libyul/AsmData.h>
|
|
#include <libyul/backends/evm/EVMDialect.h>
|
|
|
|
using namespace solidity;
|
|
using namespace solidity::langutil;
|
|
using namespace solidity::frontend;
|
|
using namespace std;
|
|
|
|
ControlFlowBuilder::ControlFlowBuilder(CFG::NodeContainer& _nodeContainer, FunctionFlow const& _functionFlow):
|
|
m_nodeContainer(_nodeContainer),
|
|
m_currentNode(_functionFlow.entry),
|
|
m_returnNode(_functionFlow.exit),
|
|
m_revertNode(_functionFlow.revert),
|
|
m_transactionReturnNode(_functionFlow.transactionReturn)
|
|
{
|
|
}
|
|
|
|
|
|
unique_ptr<FunctionFlow> ControlFlowBuilder::createFunctionFlow(
|
|
CFG::NodeContainer& _nodeContainer,
|
|
FunctionDefinition const& _function
|
|
)
|
|
{
|
|
auto functionFlow = make_unique<FunctionFlow>();
|
|
functionFlow->entry = _nodeContainer.newNode();
|
|
functionFlow->exit = _nodeContainer.newNode();
|
|
functionFlow->revert = _nodeContainer.newNode();
|
|
functionFlow->transactionReturn = _nodeContainer.newNode();
|
|
ControlFlowBuilder builder(_nodeContainer, *functionFlow);
|
|
builder.appendControlFlow(_function);
|
|
|
|
return functionFlow;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(BinaryOperation const& _operation)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
|
|
switch (_operation.getOperator())
|
|
{
|
|
case Token::Or:
|
|
case Token::And:
|
|
{
|
|
visitNode(_operation);
|
|
appendControlFlow(_operation.leftExpression());
|
|
|
|
auto nodes = splitFlow<2>();
|
|
nodes[0] = createFlow(nodes[0], _operation.rightExpression());
|
|
mergeFlow(nodes, nodes[1]);
|
|
|
|
return false;
|
|
}
|
|
default:
|
|
return ASTConstVisitor::visit(_operation);
|
|
}
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(Conditional const& _conditional)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_conditional);
|
|
|
|
_conditional.condition().accept(*this);
|
|
|
|
auto nodes = splitFlow<2>();
|
|
|
|
nodes[0] = createFlow(nodes[0], _conditional.trueExpression());
|
|
nodes[1] = createFlow(nodes[1], _conditional.falseExpression());
|
|
|
|
mergeFlow(nodes);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(TryStatement const& _tryStatement)
|
|
{
|
|
appendControlFlow(_tryStatement.externalCall());
|
|
|
|
auto nodes = splitFlow(_tryStatement.clauses().size());
|
|
for (size_t i = 0; i < _tryStatement.clauses().size(); ++i)
|
|
nodes[i] = createFlow(nodes[i], _tryStatement.clauses()[i]->block());
|
|
mergeFlow(nodes);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(IfStatement const& _ifStatement)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_ifStatement);
|
|
|
|
_ifStatement.condition().accept(*this);
|
|
|
|
auto nodes = splitFlow<2>();
|
|
nodes[0] = createFlow(nodes[0], _ifStatement.trueStatement());
|
|
|
|
if (_ifStatement.falseStatement())
|
|
{
|
|
nodes[1] = createFlow(nodes[1], *_ifStatement.falseStatement());
|
|
mergeFlow(nodes);
|
|
}
|
|
else
|
|
mergeFlow(nodes, nodes[1]);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(ForStatement const& _forStatement)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_forStatement);
|
|
|
|
if (_forStatement.initializationExpression())
|
|
_forStatement.initializationExpression()->accept(*this);
|
|
|
|
auto condition = createLabelHere();
|
|
|
|
if (_forStatement.condition())
|
|
appendControlFlow(*_forStatement.condition());
|
|
|
|
auto postPart = newLabel();
|
|
auto nodes = splitFlow<2>();
|
|
auto afterFor = nodes[1];
|
|
m_currentNode = nodes[0];
|
|
|
|
{
|
|
BreakContinueScope scope(*this, afterFor, postPart);
|
|
appendControlFlow(_forStatement.body());
|
|
}
|
|
|
|
placeAndConnectLabel(postPart);
|
|
|
|
if (auto expression = _forStatement.loopExpression())
|
|
appendControlFlow(*expression);
|
|
|
|
connect(m_currentNode, condition);
|
|
m_currentNode = afterFor;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(WhileStatement const& _whileStatement)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_whileStatement);
|
|
|
|
if (_whileStatement.isDoWhile())
|
|
{
|
|
auto afterWhile = newLabel();
|
|
auto whileBody = createLabelHere();
|
|
auto condition = newLabel();
|
|
|
|
{
|
|
BreakContinueScope scope(*this, afterWhile, condition);
|
|
appendControlFlow(_whileStatement.body());
|
|
}
|
|
|
|
placeAndConnectLabel(condition);
|
|
appendControlFlow(_whileStatement.condition());
|
|
|
|
connect(m_currentNode, whileBody);
|
|
placeAndConnectLabel(afterWhile);
|
|
}
|
|
else
|
|
{
|
|
auto whileCondition = createLabelHere();
|
|
|
|
appendControlFlow(_whileStatement.condition());
|
|
|
|
auto nodes = splitFlow<2>();
|
|
|
|
auto whileBody = nodes[0];
|
|
auto afterWhile = nodes[1];
|
|
|
|
m_currentNode = whileBody;
|
|
{
|
|
BreakContinueScope scope(*this, afterWhile, whileCondition);
|
|
appendControlFlow(_whileStatement.body());
|
|
}
|
|
|
|
connect(m_currentNode, whileCondition);
|
|
|
|
m_currentNode = afterWhile;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(Break const& _break)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
solAssert(!!m_breakJump, "");
|
|
visitNode(_break);
|
|
connect(m_currentNode, m_breakJump);
|
|
m_currentNode = newLabel();
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(Continue const& _continue)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
solAssert(!!m_continueJump, "");
|
|
visitNode(_continue);
|
|
connect(m_currentNode, m_continueJump);
|
|
m_currentNode = newLabel();
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(Throw const& _throw)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
solAssert(!!m_revertNode, "");
|
|
visitNode(_throw);
|
|
connect(m_currentNode, m_revertNode);
|
|
m_currentNode = newLabel();
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(PlaceholderStatement const&)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
solAssert(!!m_placeholderEntry, "");
|
|
solAssert(!!m_placeholderExit, "");
|
|
|
|
connect(m_currentNode, m_placeholderEntry);
|
|
m_currentNode = newLabel();
|
|
connect(m_placeholderExit, m_currentNode);
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(FunctionCall const& _functionCall)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
solAssert(!!_functionCall.expression().annotation().type, "");
|
|
|
|
if (auto functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type))
|
|
switch (functionType->kind())
|
|
{
|
|
case FunctionType::Kind::Revert:
|
|
solAssert(!!m_revertNode, "");
|
|
visitNode(_functionCall);
|
|
_functionCall.expression().accept(*this);
|
|
ASTNode::listAccept(_functionCall.arguments(), *this);
|
|
connect(m_currentNode, m_revertNode);
|
|
m_currentNode = newLabel();
|
|
return false;
|
|
case FunctionType::Kind::Require:
|
|
case FunctionType::Kind::Assert:
|
|
{
|
|
solAssert(!!m_revertNode, "");
|
|
visitNode(_functionCall);
|
|
_functionCall.expression().accept(*this);
|
|
ASTNode::listAccept(_functionCall.arguments(), *this);
|
|
connect(m_currentNode, m_revertNode);
|
|
auto nextNode = newLabel();
|
|
connect(m_currentNode, nextNode);
|
|
m_currentNode = nextNode;
|
|
return false;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return ASTConstVisitor::visit(_functionCall);
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(ModifierInvocation const& _modifierInvocation)
|
|
{
|
|
if (auto arguments = _modifierInvocation.arguments())
|
|
for (auto& argument: *arguments)
|
|
appendControlFlow(*argument);
|
|
|
|
auto modifierDefinition = dynamic_cast<ModifierDefinition const*>(
|
|
_modifierInvocation.name()->annotation().referencedDeclaration
|
|
);
|
|
if (!modifierDefinition) return false;
|
|
solAssert(!!modifierDefinition, "");
|
|
solAssert(!!m_returnNode, "");
|
|
|
|
m_placeholderEntry = newLabel();
|
|
m_placeholderExit = newLabel();
|
|
|
|
appendControlFlow(*modifierDefinition);
|
|
connect(m_currentNode, m_returnNode);
|
|
|
|
m_currentNode = m_placeholderEntry;
|
|
m_returnNode = m_placeholderExit;
|
|
|
|
m_placeholderEntry = nullptr;
|
|
m_placeholderExit = nullptr;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(FunctionDefinition const& _functionDefinition)
|
|
{
|
|
for (auto const& parameter: _functionDefinition.parameters())
|
|
appendControlFlow(*parameter);
|
|
|
|
for (auto const& returnParameter: _functionDefinition.returnParameters())
|
|
{
|
|
appendControlFlow(*returnParameter);
|
|
m_returnNode->variableOccurrences.emplace_back(
|
|
*returnParameter,
|
|
VariableOccurrence::Kind::Return
|
|
);
|
|
|
|
}
|
|
|
|
for (auto const& modifier: _functionDefinition.modifiers())
|
|
appendControlFlow(*modifier);
|
|
|
|
appendControlFlow(_functionDefinition.body());
|
|
|
|
connect(m_currentNode, m_returnNode);
|
|
m_currentNode = nullptr;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(Return const& _return)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
solAssert(!!m_returnNode, "");
|
|
visitNode(_return);
|
|
if (_return.expression())
|
|
{
|
|
appendControlFlow(*_return.expression());
|
|
// Returns with return expression are considered to be assignments to the return parameters.
|
|
for (auto returnParameter: _return.annotation().functionReturnParameters->parameters())
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
*returnParameter,
|
|
VariableOccurrence::Kind::Assignment,
|
|
_return.location()
|
|
);
|
|
}
|
|
connect(m_currentNode, m_returnNode);
|
|
m_currentNode = newLabel();
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(FunctionTypeName const& _functionTypeName)
|
|
{
|
|
visitNode(_functionTypeName);
|
|
// Do not visit the parameters and return values of a function type name.
|
|
// We do not want to consider them as variable declarations for the control flow graph.
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(InlineAssembly const& _inlineAssembly)
|
|
{
|
|
solAssert(!!m_currentNode && !m_inlineAssembly, "");
|
|
|
|
m_inlineAssembly = &_inlineAssembly;
|
|
(*this)(_inlineAssembly.operations());
|
|
m_inlineAssembly = nullptr;
|
|
|
|
return false;
|
|
}
|
|
|
|
void ControlFlowBuilder::visit(yul::Statement const& _statement)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, locationOf(_statement));
|
|
ASTWalker::visit(_statement);
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::If const& _if)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
visit(*_if.condition);
|
|
|
|
auto nodes = splitFlow<2>();
|
|
m_currentNode = nodes[0];
|
|
(*this)(_if.body);
|
|
nodes[0] = m_currentNode;
|
|
mergeFlow(nodes, nodes[1]);
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::Switch const& _switch)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
visit(*_switch.expression);
|
|
|
|
auto beforeSwitch = m_currentNode;
|
|
|
|
auto nodes = splitFlow(_switch.cases.size());
|
|
for (size_t i = 0u; i < _switch.cases.size(); ++i)
|
|
{
|
|
m_currentNode = nodes[i];
|
|
(*this)(_switch.cases[i].body);
|
|
nodes[i] = m_currentNode;
|
|
}
|
|
mergeFlow(nodes);
|
|
|
|
bool hasDefault = util::contains_if(_switch.cases, [](yul::Case const& _case) { return !_case.value; });
|
|
if (!hasDefault)
|
|
connect(beforeSwitch, m_currentNode);
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::ForLoop const& _forLoop)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
|
|
(*this)(_forLoop.pre);
|
|
|
|
auto condition = createLabelHere();
|
|
|
|
if (_forLoop.condition)
|
|
visit(*_forLoop.condition);
|
|
|
|
auto loopExpression = newLabel();
|
|
auto nodes = splitFlow<2>();
|
|
auto afterFor = nodes[1];
|
|
m_currentNode = nodes[0];
|
|
|
|
{
|
|
BreakContinueScope scope(*this, afterFor, loopExpression);
|
|
(*this)(_forLoop.body);
|
|
}
|
|
|
|
placeAndConnectLabel(loopExpression);
|
|
|
|
(*this)(_forLoop.post);
|
|
|
|
connect(m_currentNode, condition);
|
|
m_currentNode = afterFor;
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::Break const&)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
solAssert(m_breakJump, "");
|
|
connect(m_currentNode, m_breakJump);
|
|
m_currentNode = newLabel();
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::Continue const&)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
solAssert(m_continueJump, "");
|
|
connect(m_currentNode, m_continueJump);
|
|
m_currentNode = newLabel();
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::Identifier const& _identifier)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
auto const& externalReferences = m_inlineAssembly->annotation().externalReferences;
|
|
if (externalReferences.count(&_identifier))
|
|
{
|
|
if (auto const* declaration = dynamic_cast<VariableDeclaration const*>(externalReferences.at(&_identifier).declaration))
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
*declaration,
|
|
VariableOccurrence::Kind::Access,
|
|
_identifier.location
|
|
);
|
|
}
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::Assignment const& _assignment)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
visit(*_assignment.value);
|
|
auto const& externalReferences = m_inlineAssembly->annotation().externalReferences;
|
|
for (auto const& variable: _assignment.variableNames)
|
|
if (externalReferences.count(&variable))
|
|
if (auto const* declaration = dynamic_cast<VariableDeclaration const*>(externalReferences.at(&variable).declaration))
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
*declaration,
|
|
VariableOccurrence::Kind::Assignment,
|
|
variable.location
|
|
);
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::FunctionCall const& _functionCall)
|
|
{
|
|
using namespace yul;
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
yul::ASTWalker::operator()(_functionCall);
|
|
|
|
if (auto const *builtinFunction = m_inlineAssembly->dialect().builtin(_functionCall.functionName.name))
|
|
if (builtinFunction->controlFlowSideEffects.terminates)
|
|
{
|
|
if (builtinFunction->controlFlowSideEffects.reverts)
|
|
connect(m_currentNode, m_revertNode);
|
|
else
|
|
connect(m_currentNode, m_transactionReturnNode);
|
|
m_currentNode = newLabel();
|
|
}
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::FunctionDefinition const&)
|
|
{
|
|
solAssert(m_currentNode && m_inlineAssembly, "");
|
|
// External references cannot be accessed from within functions, so we can ignore their control flow.
|
|
// TODO: we might still want to track if they always revert or return, though.
|
|
}
|
|
|
|
void ControlFlowBuilder::operator()(yul::Leave const&)
|
|
{
|
|
// This has to be implemented, if we ever decide to visit functions.
|
|
solUnimplementedAssert(false, "");
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(VariableDeclaration const& _variableDeclaration)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_variableDeclaration);
|
|
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
_variableDeclaration,
|
|
VariableOccurrence::Kind::Declaration
|
|
);
|
|
|
|
// Handle declaration with immediate assignment.
|
|
if (_variableDeclaration.value())
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
_variableDeclaration,
|
|
VariableOccurrence::Kind::Assignment,
|
|
_variableDeclaration.value()->location()
|
|
);
|
|
// Function arguments are considered to be immediately assigned as well (they are "externally assigned").
|
|
else if (_variableDeclaration.isCallableOrCatchParameter() && !_variableDeclaration.isReturnParameter())
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
_variableDeclaration,
|
|
VariableOccurrence::Kind::Assignment
|
|
);
|
|
return true;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_variableDeclarationStatement);
|
|
|
|
for (auto const& var: _variableDeclarationStatement.declarations())
|
|
if (var)
|
|
var->accept(*this);
|
|
if (_variableDeclarationStatement.initialValue())
|
|
{
|
|
_variableDeclarationStatement.initialValue()->accept(*this);
|
|
for (size_t i = 0; i < _variableDeclarationStatement.declarations().size(); i++)
|
|
if (auto const& var = _variableDeclarationStatement.declarations()[i])
|
|
{
|
|
auto expression = _variableDeclarationStatement.initialValue();
|
|
if (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
|
|
if (tupleExpression->components().size() > 1)
|
|
{
|
|
solAssert(tupleExpression->components().size() > i, "");
|
|
expression = tupleExpression->components()[i].get();
|
|
}
|
|
while (auto tupleExpression = dynamic_cast<TupleExpression const*>(expression))
|
|
if (tupleExpression->components().size() == 1)
|
|
expression = tupleExpression->components().front().get();
|
|
else
|
|
break;
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
*var,
|
|
VariableOccurrence::Kind::Assignment,
|
|
expression ? std::make_optional(expression->location()) : std::optional<langutil::SourceLocation>{}
|
|
);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visit(Identifier const& _identifier)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
visitNode(_identifier);
|
|
|
|
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
|
|
m_currentNode->variableOccurrences.emplace_back(
|
|
*variableDeclaration,
|
|
static_cast<Expression const&>(_identifier).annotation().willBeWrittenTo ?
|
|
VariableOccurrence::Kind::Assignment :
|
|
VariableOccurrence::Kind::Access,
|
|
_identifier.location()
|
|
);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ControlFlowBuilder::visitNode(ASTNode const& _node)
|
|
{
|
|
solAssert(!!m_currentNode, "");
|
|
m_currentNode->location = langutil::SourceLocation::smallestCovering(m_currentNode->location, _node.location());
|
|
return true;
|
|
}
|
|
|
|
void ControlFlowBuilder::appendControlFlow(ASTNode const& _node)
|
|
{
|
|
_node.accept(*this);
|
|
}
|
|
|
|
CFGNode* ControlFlowBuilder::createFlow(CFGNode* _entry, ASTNode const& _node)
|
|
{
|
|
auto oldCurrentNode = m_currentNode;
|
|
m_currentNode = _entry;
|
|
appendControlFlow(_node);
|
|
auto endNode = m_currentNode;
|
|
m_currentNode = oldCurrentNode;
|
|
return endNode;
|
|
}
|
|
|
|
void ControlFlowBuilder::connect(CFGNode* _from, CFGNode* _to)
|
|
{
|
|
solAssert(_from, "");
|
|
solAssert(_to, "");
|
|
_from->exits.push_back(_to);
|
|
_to->entries.push_back(_from);
|
|
}
|
|
|
|
CFGNode* ControlFlowBuilder::newLabel()
|
|
{
|
|
return m_nodeContainer.newNode();
|
|
}
|
|
|
|
CFGNode* ControlFlowBuilder::createLabelHere()
|
|
{
|
|
auto label = m_nodeContainer.newNode();
|
|
connect(m_currentNode, label);
|
|
m_currentNode = label;
|
|
return label;
|
|
}
|
|
|
|
void ControlFlowBuilder::placeAndConnectLabel(CFGNode* _node)
|
|
{
|
|
connect(m_currentNode, _node);
|
|
m_currentNode = _node;
|
|
}
|
|
|
|
ControlFlowBuilder::BreakContinueScope::BreakContinueScope(
|
|
ControlFlowBuilder& _parser,
|
|
CFGNode* _breakJump,
|
|
CFGNode* _continueJump
|
|
): m_parser(_parser), m_origBreakJump(_parser.m_breakJump), m_origContinueJump(_parser.m_continueJump)
|
|
{
|
|
m_parser.m_breakJump = _breakJump;
|
|
m_parser.m_continueJump = _continueJump;
|
|
}
|
|
|
|
ControlFlowBuilder::BreakContinueScope::~BreakContinueScope()
|
|
{
|
|
m_parser.m_breakJump = m_origBreakJump;
|
|
m_parser.m_continueJump = m_origContinueJump;
|
|
}
|