Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth
2020-12-08 17:42:31 +01:00
64 changed files with 863 additions and 215 deletions
+34 -4
View File
@@ -202,7 +202,7 @@ bool CHC::visit(FunctionDefinition const& _function)
initFunction(_function);
auto functionEntryBlock = createBlock(m_currentFunction, PredicateType::FunctionEntry);
auto functionEntryBlock = createBlock(m_currentFunction, PredicateType::FunctionBlock);
auto bodyBlock = createBlock(&m_currentFunction->body(), PredicateType::FunctionBlock);
auto functionPred = predicate(*functionEntryBlock);
@@ -469,6 +469,8 @@ void CHC::endVisit(Break const& _break)
{
solAssert(m_breakDest, "");
connectBlocks(m_currentBlock, predicate(*m_breakDest));
// Add an unreachable ghost node to collect unreachable statements after a break.
auto breakGhost = createBlock(&_break, PredicateType::FunctionBlock, "break_ghost_");
m_currentBlock = predicate(*breakGhost);
}
@@ -477,6 +479,8 @@ void CHC::endVisit(Continue const& _continue)
{
solAssert(m_continueDest, "");
connectBlocks(m_currentBlock, predicate(*m_continueDest));
// Add an unreachable ghost node to collect unreachable statements after a continue.
auto continueGhost = createBlock(&_continue, PredicateType::FunctionBlock, "continue_ghost_");
m_currentBlock = predicate(*continueGhost);
}
@@ -511,6 +515,32 @@ void CHC::endVisit(IndexRangeAccess const& _range)
m_context.addAssertion(sliceArray->length() == end - start);
}
void CHC::endVisit(Return const& _return)
{
SMTEncoder::endVisit(_return);
connectBlocks(m_currentBlock, predicate(*m_returnDests.back()));
// Add an unreachable ghost node to collect unreachable statements after a return.
auto returnGhost = createBlock(&_return, PredicateType::FunctionBlock, "return_ghost_");
m_currentBlock = predicate(*returnGhost);
}
void CHC::pushInlineFrame(CallableDeclaration const& _callable)
{
m_returnDests.push_back(createBlock(&_callable, PredicateType::FunctionBlock, "return_"));
}
void CHC::popInlineFrame(CallableDeclaration const& _callable)
{
solAssert(!m_returnDests.empty(), "");
auto const& ret = *m_returnDests.back();
solAssert(ret.programNode() == &_callable, "");
connectBlocks(m_currentBlock, predicate(ret));
setCurrentBlock(ret);
m_returnDests.pop_back();
}
void CHC::visitAssert(FunctionCall const& _funCall)
{
auto const& args = _funCall.arguments();
@@ -756,6 +786,7 @@ void CHC::resetContractAnalysis()
m_unknownFunctionCallSeen = false;
m_breakDest = nullptr;
m_continueDest = nullptr;
m_returnDests.clear();
errorFlag().resetIndex();
}
@@ -806,7 +837,7 @@ set<unsigned> CHC::transactionVerificationTargetsIds(ASTNode const* _txRoot)
SortPointer CHC::sort(FunctionDefinition const& _function)
{
return functionSort(_function, m_currentContract, state());
return functionBodySort(_function, m_currentContract, state());
}
SortPointer CHC::sort(ASTNode const* _node)
@@ -1079,7 +1110,6 @@ smtutil::Expression CHC::predicate(Predicate const& _block)
return ::interface(_block, *m_currentContract, m_context);
case PredicateType::ConstructorSummary:
return constructor(_block, m_context);
case PredicateType::FunctionEntry:
case PredicateType::FunctionSummary:
return smt::function(_block, m_currentContract, m_context);
case PredicateType::FunctionBlock:
@@ -1228,7 +1258,7 @@ void CHC::verificationTargetEncountered(
connectBlocks(
m_currentBlock,
predicate(*m_errorDest),
currentPathConditions() && _errorCondition && errorFlag().currentValue() == errorId
_errorCondition && errorFlag().currentValue() == errorId
);
m_context.addAssertion(errorFlag().currentValue() == previousError);
+10
View File
@@ -83,6 +83,10 @@ private:
void endVisit(Break const& _node) override;
void endVisit(Continue const& _node) override;
void endVisit(IndexRangeAccess const& _node) override;
void endVisit(Return const& _node) override;
void pushInlineFrame(CallableDeclaration const& _callable) override;
void popInlineFrame(CallableDeclaration const& _callable) override;
void visitAssert(FunctionCall const& _funCall);
void visitAddMulMod(FunctionCall const& _funCall) override;
@@ -333,6 +337,12 @@ private:
/// 2) Constructor summary, if error happens while evaluating base constructor arguments.
/// 3) Function summary, if error happens inside a function.
Predicate const* m_errorDest = nullptr;
/// Represents the stack of destinations where a `return` should go.
/// This is different from `m_errorDest` above:
/// - Constructor initializers and constructor summaries will never be `return` targets because they are artificial.
/// - Modifiers also have their own `return` target blocks, whereas they do not have their own error destination.
std::vector<Predicate const*> m_returnDests;
//@}
/// CHC solver.
-1
View File
@@ -35,7 +35,6 @@ enum class PredicateType
Interface,
NondetInterface,
ConstructorSummary,
FunctionEntry,
FunctionSummary,
FunctionBlock,
Error,
+27 -16
View File
@@ -152,10 +152,12 @@ void SMTEncoder::visitFunctionOrModifier()
if (m_modifierDepthStack.back() == static_cast<int>(function.modifiers().size()))
{
pushPathCondition(currentPathConditions());
if (function.isImplemented())
{
pushInlineFrame(function);
function.body().accept(*this);
popPathCondition();
popInlineFrame(function);
}
}
else
{
@@ -192,7 +194,7 @@ void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation,
initializeFunctionCallParameters(*_definition, args);
pushCallStack({_definition, _invocation});
pushPathCondition(currentPathConditions());
pushInlineFrame(*_definition);
if (auto modifier = dynamic_cast<ModifierDefinition const*>(_definition))
{
if (modifier->isImplemented())
@@ -205,7 +207,7 @@ void SMTEncoder::inlineModifierInvocation(ModifierInvocation const* _invocation,
function->accept(*this);
// Functions are popped from the callstack in endVisit(FunctionDefinition)
}
popPathCondition();
popInlineFrame(*_definition);
}
void SMTEncoder::inlineConstructorHierarchy(ContractDefinition const& _contract)
@@ -289,6 +291,16 @@ bool SMTEncoder::visit(TryCatchClause const& _clause)
return false;
}
void SMTEncoder::pushInlineFrame(CallableDeclaration const&)
{
pushPathCondition(currentPathConditions());
}
void SMTEncoder::popInlineFrame(CallableDeclaration const&)
{
popPathCondition();
}
void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
{
if (_varDecl.declarations().size() != 1)
@@ -942,16 +954,21 @@ void SMTEncoder::visitTypeConversion(FunctionCall const& _funCall)
solAssert(_funCall.arguments().size() == 1, "");
auto argument = _funCall.arguments().front();
auto const& argType = argument->annotation().type;
auto const argType = argument->annotation().type;
auto const funCallType = _funCall.annotation().type;
unsigned argSize = argument->annotation().type->storageBytes();
unsigned castSize = _funCall.annotation().type->storageBytes();
auto symbArg = expr(*argument, funCallType);
auto const& funCallType = _funCall.annotation().type;
if (smt::isStringLiteral(*argType) && smt::isFixedBytes(*funCallType))
{
defineExpr(_funCall, symbArg);
return;
}
// TODO Simplify this whole thing for 0.8.0 where weird casts are disallowed.
auto symbArg = expr(*argument, funCallType);
unsigned argSize = argType->storageBytes();
unsigned castSize = funCallType->storageBytes();
bool castIsSigned = smt::isNumber(*funCallType) && smt::isSigned(funCallType);
bool argIsSigned = smt::isNumber(*argType) && smt::isSigned(argType);
optional<smtutil::Expression> symbMin;
@@ -1095,7 +1112,7 @@ void SMTEncoder::endVisit(Literal const& _literal)
addArrayLiteralAssertions(
*symbArray,
applyMap(_literal.value(), [&](auto const& c) { return smtutil::Expression{size_t(c)}; })
applyMap(_literal.value(), [](unsigned char c) { return smtutil::Expression{size_t(c)}; })
);
}
else
@@ -1953,12 +1970,6 @@ smtutil::Expression SMTEncoder::compoundAssignment(Assignment const& _assignment
auto decl = identifierToVariable(_assignment.leftHandSide());
TypePointer commonType = Type::commonType(
_assignment.leftHandSide().annotation().type,
_assignment.rightHandSide().annotation().type
);
solAssert(commonType == _assignment.annotation().type, "");
if (compoundToBitwise.count(op))
return bitwiseOperation(
compoundToBitwise.at(op),
+3
View File
@@ -118,6 +118,9 @@ protected:
void endVisit(Continue const&) override {}
bool visit(TryCatchClause const& _node) override;
virtual void pushInlineFrame(CallableDeclaration const&);
virtual void popInlineFrame(CallableDeclaration const&);
/// Do not visit subtree if node is a RationalNumber.
/// Symbolic _expr is the rational literal.
bool shortcutRationalNumber(Expression const& _expr);
+4 -2
View File
@@ -566,11 +566,13 @@ optional<smtutil::Expression> symbolicTypeConversion(TypePointer _from, TypePoin
// but they can also be compared/assigned to fixed bytes, in which
// case they'd need to be encoded as numbers.
if (auto strType = dynamic_cast<StringLiteralType const*>(_from))
if (_to->category() == frontend::Type::Category::FixedBytes)
if (auto fixedBytesType = dynamic_cast<FixedBytesType const*>(_to))
{
if (strType->value().empty())
return smtutil::Expression(size_t(0));
return smtutil::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add)));
auto bytesVec = util::asBytes(strType->value());
bytesVec.resize(fixedBytesType->numBytes(), 0);
return smtutil::Expression(u256(toHex(bytesVec, util::HexPrefix::Add)));
}
return std::nullopt;
+51 -14
View File
@@ -172,16 +172,21 @@ bool hashMatchesContent(string const& _hash, string const& _content)
bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact, bool _wildcardMatchesExperimental)
{
static set<string> experimental{"ir", "irOptimized", "wast", "ewasm", "ewasm.wast"};
for (auto const& artifact: _outputSelection)
/// @TODO support sub-matching, e.g "evm" matches "evm.assembly"
if (artifact == _artifact)
for (auto const& selectedArtifactJson: _outputSelection)
{
string const& selectedArtifact = selectedArtifactJson.asString();
if (
_artifact == selectedArtifact ||
boost::algorithm::starts_with(_artifact, selectedArtifact + ".")
)
return true;
else if (artifact == "*")
else if (selectedArtifact == "*")
{
// "ir", "irOptimized", "wast" and "ewasm.wast" can only be matched by "*" if activated.
if (experimental.count(_artifact) == 0 || _wildcardMatchesExperimental)
return true;
}
}
return false;
}
@@ -371,17 +376,23 @@ Json::Value collectEVMObject(
evmasm::LinkerObject const& _object,
string const* _sourceMap,
Json::Value _generatedSources,
bool _runtimeObject
bool _runtimeObject,
function<bool(string)> const& _artifactRequested
)
{
Json::Value output = Json::objectValue;
output["object"] = _object.toHex();
output["opcodes"] = evmasm::disassemble(_object.bytecode);
output["sourceMap"] = _sourceMap ? *_sourceMap : "";
output["linkReferences"] = formatLinkReferences(_object.linkReferences);
if (_runtimeObject)
if (_artifactRequested("object"))
output["object"] = _object.toHex();
if (_artifactRequested("opcodes"))
output["opcodes"] = evmasm::disassemble(_object.bytecode);
if (_artifactRequested("sourceMap"))
output["sourceMap"] = _sourceMap ? *_sourceMap : "";
if (_artifactRequested("linkReferences"))
output["linkReferences"] = formatLinkReferences(_object.linkReferences);
if (_runtimeObject && _artifactRequested("immutableReferences"))
output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences);
output["generatedSources"] = move(_generatedSources);
if (_artifactRequested("generatedSources"))
output["generatedSources"] = move(_generatedSources);
return output;
}
@@ -1146,7 +1157,14 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
compilerStack.object(contractName),
compilerStack.sourceMapping(contractName),
compilerStack.generatedSources(contractName),
false
false,
[&](string const& _element) { return isArtifactRequested(
_inputsAndSettings.outputSelection,
file,
name,
"evm.bytecode." + _element,
wildcardMatchesExperimental
); }
);
if (compilationSuccess && isArtifactRequested(
@@ -1160,7 +1178,14 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
compilerStack.runtimeObject(contractName),
compilerStack.runtimeSourceMapping(contractName),
compilerStack.generatedSources(contractName, true),
true
true,
[&](string const& _element) { return isArtifactRequested(
_inputsAndSettings.outputSelection,
file,
name,
"evm.deployedBytecode." + _element,
wildcardMatchesExperimental
); }
);
if (!evmData.empty())
@@ -1257,7 +1282,19 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
MachineAssemblyObject const& o = objectKind == "bytecode" ? object : runtimeObject;
if (o.bytecode)
output["contracts"][sourceName][contractName]["evm"][objectKind] =
collectEVMObject(*o.bytecode, o.sourceMappings.get(), Json::arrayValue, false);
collectEVMObject(
*o.bytecode,
o.sourceMappings.get(),
Json::arrayValue,
false,
[&](string const& _element) { return isArtifactRequested(
_inputsAndSettings.outputSelection,
sourceName,
contractName,
"evm." + objectKind + "." + _element,
wildcardMatchesExperimental
); }
);
}
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental))