mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -35,7 +35,6 @@ enum class PredicateType
|
||||
Interface,
|
||||
NondetInterface,
|
||||
ConstructorSummary,
|
||||
FunctionEntry,
|
||||
FunctionSummary,
|
||||
FunctionBlock,
|
||||
Error,
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user