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

This commit is contained in:
chriseth
2020-07-06 15:25:25 +02:00
118 changed files with 1713 additions and 380 deletions
+32 -10
View File
@@ -158,7 +158,8 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
else if (_variable.isStateVariable())
{
set<StructDefinition const*> structsSeen;
if (structureSizeEstimate(*_variable.type(), structsSeen) >= bigint(1) << 64)
TypeSet oversizedSubTypes;
if (structureSizeEstimate(*_variable.type(), structsSeen, oversizedSubTypes) >= bigint(1) << 64)
m_errorReporter.warning(
3408_error,
_variable.location(),
@@ -166,6 +167,14 @@ bool StaticAnalyzer::visit(VariableDeclaration const& _variable)
"Either use mappings or dynamic arrays and allow their size to be increased only "
"in small quantities per transaction."
);
for (Type const* type: oversizedSubTypes)
m_errorReporter.warning(
7325_error,
_variable.location(),
"Type " + type->canonicalName() + " has large size and thus makes collisions likely. "
"Either use mappings or dynamic arrays and allow their size to be increased only "
"in small quantities per transaction."
);
}
return true;
}
@@ -339,30 +348,43 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
return true;
}
bigint StaticAnalyzer::structureSizeEstimate(Type const& _type, set<StructDefinition const*>& _structsSeen)
bigint StaticAnalyzer::structureSizeEstimate(
Type const& _type,
set<StructDefinition const*>& _structsSeen,
TypeSet& _oversizedSubTypes
)
{
switch (_type.category())
{
case Type::Category::Array:
{
auto const& t = dynamic_cast<ArrayType const&>(_type);
return structureSizeEstimate(*t.baseType(), _structsSeen) * (t.isDynamicallySized() ? 1 : t.length());
bigint baseTypeSize = structureSizeEstimate(*t.baseType(), _structsSeen, _oversizedSubTypes);
if (baseTypeSize >= bigint(1) << 64)
_oversizedSubTypes.insert(t.baseType());
if (!t.isDynamicallySized())
return structureSizeEstimate(*t.baseType(), _structsSeen, _oversizedSubTypes) * t.length();
break;
}
case Type::Category::Struct:
{
auto const& t = dynamic_cast<StructType const&>(_type);
bigint size = 1;
if (!_structsSeen.count(&t.structDefinition()))
{
_structsSeen.insert(&t.structDefinition());
for (auto const& m: t.members(nullptr))
size += structureSizeEstimate(*m.type, _structsSeen);
}
if (_structsSeen.count(&t.structDefinition()))
return size;
_structsSeen.insert(&t.structDefinition());
for (auto const& m: t.members(nullptr))
size += structureSizeEstimate(*m.type, _structsSeen, _oversizedSubTypes);
_structsSeen.erase(&t.structDefinition());
return size;
}
case Type::Category::Mapping:
{
return structureSizeEstimate(*dynamic_cast<MappingType const&>(_type).valueType(), _structsSeen);
auto const* valueType = dynamic_cast<MappingType const&>(_type).valueType();
bigint valueTypeSize = structureSizeEstimate(*valueType, _structsSeen, _oversizedSubTypes);
if (valueTypeSize >= bigint(1) << 64)
_oversizedSubTypes.insert(valueType);
break;
}
default:
break;
+15 -1
View File
@@ -73,8 +73,22 @@ private:
bool visit(BinaryOperation const& _operation) override;
bool visit(FunctionCall const& _functionCall) override;
struct TypeComp
{
bool operator()(Type const* lhs, Type const* rhs) const
{
solAssert(lhs && rhs, "");
return lhs->richIdentifier() < rhs->richIdentifier();
}
};
using TypeSet = std::set<Type const*, TypeComp>;
/// @returns the size of this type in storage, including all sub-types.
static bigint structureSizeEstimate(Type const& _type, std::set<StructDefinition const*>& _structsSeen);
static bigint structureSizeEstimate(
Type const& _type,
std::set<StructDefinition const*>& _structsSeen,
TypeSet& _oversizedSubTypes
);
langutil::ErrorReporter& m_errorReporter;
+2 -1
View File
@@ -226,8 +226,9 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
else
solUnimplemented("Copying of type " + _sourceType.toString(false) + " to storage not yet supported.");
// stack: target_ref target_data_end source_data_pos target_data_pos source_data_end [target_byte_offset] [source_byte_offset] <source_value>...
solAssert(
assertThrow(
2 + byteOffsetSize + sourceBaseType->sizeOnStack() <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
// fetch target storage reference
+1 -1
View File
@@ -404,7 +404,7 @@ void CompilerContext::appendInlineAssembly(
stackDiff -= 1;
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_identifier.location) <<
util::errinfo_comment("Stack too deep (" + to_string(stackDiff) + "), try removing local variables.")
);
+23 -6
View File
@@ -455,7 +455,11 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++;
solAssert((argSize + dynPointers) < 16, "Stack too deep, try using fewer variables.");
assertThrow(
(argSize + dynPointers) < 16,
StackTooDeepError,
"Stack too deep, try using fewer variables."
);
}
else
{
@@ -507,8 +511,9 @@ void CompilerUtils::encodeToMemory(
if (targetType->isDynamicallySized() && !_copyDynamicDataInPlace)
{
// copy tail pointer (=mem_end - mem_start) to memory
solAssert(
assertThrow(
(2 + dynPointers) <= 16,
StackTooDeepError,
"Stack too deep(" + to_string(2 + dynPointers) + "), try using fewer variables."
);
m_context << dupInstruction(2 + dynPointers) << Instruction::DUP2;
@@ -1289,7 +1294,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
// move variable starting from its top end in the stack
if (stackPosition - size + 1 > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_variable.location()) <<
util::errinfo_comment("Stack too deep, try removing local variables.")
);
@@ -1299,7 +1304,11 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize)
{
solAssert(_stackDepth <= 16, "Stack too deep, try removing local variables.");
assertThrow(
_stackDepth <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 0; i < _itemSize; ++i)
m_context << dupInstruction(_stackDepth);
}
@@ -1321,14 +1330,22 @@ void CompilerUtils::moveIntoStack(unsigned _stackDepth, unsigned _itemSize)
void CompilerUtils::rotateStackUp(unsigned _items)
{
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
assertThrow(
_items - 1 <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(_items - i);
}
void CompilerUtils::rotateStackDown(unsigned _items)
{
solAssert(_items - 1 <= 16, "Stack too deep, try removing local variables.");
assertThrow(
_items - 1 <= 16,
StackTooDeepError,
"Stack too deep, try removing local variables."
);
for (unsigned i = 1; i < _items; ++i)
m_context << swapInstruction(i);
}
+3 -3
View File
@@ -634,7 +634,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
if (stackLayout.size() > 17)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_function.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
@@ -798,7 +798,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
solAssert(variable->type()->sizeOnStack() == 1, "");
if (stackDiff < 1 || stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
@@ -831,7 +831,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
unsigned stackDiff = static_cast<unsigned>(_assembly.stackHeight()) - m_context.baseStackOffsetOfVariable(*variable) - 1;
if (stackDiff > 16 || stackDiff < 1)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_inlineAssembly.location()) <<
errinfo_comment("Stack too deep(" + to_string(stackDiff) + "), try removing local variables.")
);
+2 -2
View File
@@ -226,7 +226,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), "");
if (retSizeOnStack > 15)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_varDecl.location()) <<
errinfo_comment("Stack too deep.")
);
@@ -308,7 +308,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
{
if (itemSize + lvalueSize > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_assignment.location()) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
+2 -2
View File
@@ -47,7 +47,7 @@ void StackVariable::retrieveValue(SourceLocation const& _location, bool) const
unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset);
if (stackPos + 1 > 16) //@todo correct this by fetching earlier or moving to memory
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
@@ -61,7 +61,7 @@ void StackVariable::storeValue(Type const&, SourceLocation const& _location, boo
unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1;
if (stackDiff > 16)
BOOST_THROW_EXCEPTION(
CompilerError() <<
StackTooDeepError() <<
errinfo_sourceLocation(_location) <<
errinfo_comment("Stack too deep, try removing local variables.")
);
+20 -4
View File
@@ -1646,13 +1646,30 @@ string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType cons
});
}
string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type)
string YulUtilFunctions::allocateMemoryStructFunction(StructType const& _type)
{
string functionName = "allocate_and_initialize_memory_struct_" + _type.identifier();
string functionName = "allocate_memory_struct_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
Whiskers templ(R"(
function <functionName>() -> memPtr {
memPtr := <alloc>(<allocSize>)
}
)");
templ("functionName", functionName);
templ("alloc", allocationFunction());
templ("allocSize", _type.memoryDataSize().str());
return templ.render();
});
}
string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type)
{
string functionName = "allocate_and_zero_memory_struct_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
Whiskers templ(R"(
function <functionName>() -> memPtr {
memPtr := <allocStruct>()
let offset := memPtr
<#member>
mstore(offset, <zeroValue>())
@@ -1661,10 +1678,9 @@ string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType co
}
)");
templ("functionName", functionName);
templ("alloc", allocationFunction());
templ("allocStruct", allocateMemoryStructFunction(_type));
TypePointers const& members = _type.memoryMemberTypes();
templ("allocSize", _type.memoryDataSize().str());
vector<map<string, string>> memberParams(members.size());
for (size_t i = 0; i < members.size(); ++i)
+6 -1
View File
@@ -286,8 +286,13 @@ public:
/// signature: (length) -> memPtr
std::string allocateAndInitializeMemoryArrayFunction(ArrayType const& _type);
/// @returns the name of a function that allocates a memory struct (no
/// initialization takes place).
/// signature: () -> memPtr
std::string allocateMemoryStructFunction(StructType const& _type);
/// @returns the name of a function that allocates and zeroes a memory struct.
/// signature: (members) -> memPtr
/// signature: () -> memPtr
std::string allocateAndInitializeMemoryStructFunction(StructType const& _type);
/// @returns the name of the function that converts a value of type @a _from
@@ -600,22 +600,30 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
{
solUnimplementedAssert(
_functionCall.annotation().kind == FunctionCallKind::FunctionCall ||
_functionCall.annotation().kind == FunctionCallKind::TypeConversion,
_functionCall.annotation().kind != FunctionCallKind::Unset,
"This type of function call is not yet implemented"
);
Type const& funcType = type(_functionCall.expression());
if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion)
{
solAssert(funcType.category() == Type::Category::TypeType, "Expected category to be TypeType");
solAssert(
_functionCall.expression().annotation().type->category() == Type::Category::TypeType,
"Expected category to be TypeType"
);
solAssert(_functionCall.arguments().size() == 1, "Expected one argument for type conversion");
define(_functionCall, *_functionCall.arguments().front());
return;
}
FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&funcType);
FunctionTypePointer functionType = nullptr;
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
functionType = structType.constructorType();
}
else
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
TypePointers parameterTypes = functionType->parameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
@@ -639,6 +647,34 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
arguments.push_back(callArguments[static_cast<size_t>(std::distance(callArgumentNames.begin(), it))]);
}
if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall)
{
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
define(_functionCall) << m_utils.allocateMemoryStructFunction(structType) << "()\n";
MemberList::MemberMap members = structType.nativeMembers(nullptr);
solAssert(members.size() == arguments.size(), "Struct parameter mismatch.");
for (size_t i = 0; i < arguments.size(); i++)
{
IRVariable converted = convert(*arguments[i], *parameterTypes[i]);
m_code <<
m_utils.writeToMemoryFunction(*functionType->parameterTypes()[i]) <<
"(add(" <<
IRVariable(_functionCall).part("mpos").name() <<
", " <<
structType.memoryOffsetOfMember(members[i].name) <<
"), " <<
converted.commaSeparatedList() <<
")\n";
}
return;
}
auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression());
if (memberAccess)
{
+141 -33
View File
@@ -197,8 +197,11 @@ void CHC::endVisit(ContractDefinition const& _contract)
bool CHC::visit(FunctionDefinition const& _function)
{
if (!shouldVisit(_function))
if (!_function.isImplemented())
{
connectBlocks(genesis(), summary(_function));
return false;
}
// This is the case for base constructor inlining.
if (m_currentFunction)
@@ -243,7 +246,7 @@ bool CHC::visit(FunctionDefinition const& _function)
void CHC::endVisit(FunctionDefinition const& _function)
{
if (!shouldVisit(_function))
if (!_function.isImplemented())
return;
// This is the case for base constructor inlining.
@@ -474,11 +477,14 @@ void CHC::endVisit(FunctionCall const& _funCall)
internalFunctionCall(_funCall);
break;
case FunctionType::Kind::External:
case FunctionType::Kind::BareStaticCall:
externalFunctionCall(_funCall);
SMTEncoder::endVisit(_funCall);
break;
case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
case FunctionType::Kind::Creation:
case FunctionType::Kind::KECCAK256:
case FunctionType::Kind::ECRecover:
@@ -574,6 +580,35 @@ void CHC::internalFunctionCall(FunctionCall const& _funCall)
m_context.addAssertion(m_error.currentValue() == previousError);
}
void CHC::externalFunctionCall(FunctionCall const& _funCall)
{
solAssert(m_currentContract, "");
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
auto kind = funType.kind();
solAssert(kind == FunctionType::Kind::External || kind == FunctionType::Kind::BareStaticCall, "");
auto const* function = functionCallToDefinition(_funCall);
if (!function)
return;
for (auto var: function->returnParameters())
m_context.variable(*var)->increaseIndex();
auto preCallState = currentStateVariables();
bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall ||
function->stateMutability() == StateMutability::Pure ||
function->stateMutability() == StateMutability::View;
if (!usesStaticCall)
for (auto const* var: m_stateVariables)
m_context.variable(*var)->increaseIndex();
auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + currentStateVariables());
m_context.addAssertion(nondet);
m_context.addAssertion(m_error.currentValue() == 0);
}
void CHC::unknownFunctionCall(FunctionCall const&)
{
/// Function calls are not handled at the moment,
@@ -651,11 +686,6 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c
}
}
bool CHC::shouldVisit(FunctionDefinition const& _function) const
{
return _function.isImplemented();
}
void CHC::setCurrentBlock(
smt::SymbolicFunctionVariable const& _block,
vector<smtutil::Expression> const* _arguments
@@ -710,10 +740,14 @@ smtutil::SortPointer CHC::constructorSort()
smtutil::SortPointer CHC::interfaceSort()
{
return make_shared<smtutil::FunctionSort>(
m_stateSorts,
smtutil::SortProvider::boolSort
);
solAssert(m_currentContract, "");
return interfaceSort(*m_currentContract);
}
smtutil::SortPointer CHC::nondetInterfaceSort()
{
solAssert(m_currentContract, "");
return nondetInterfaceSort(*m_currentContract);
}
smtutil::SortPointer CHC::interfaceSort(ContractDefinition const& _contract)
@@ -724,6 +758,15 @@ smtutil::SortPointer CHC::interfaceSort(ContractDefinition const& _contract)
);
}
smtutil::SortPointer CHC::nondetInterfaceSort(ContractDefinition const& _contract)
{
auto sorts = stateSorts(_contract);
return make_shared<smtutil::FunctionSort>(
sorts + sorts,
smtutil::SortProvider::boolSort
);
}
smtutil::SortPointer CHC::arity0FunctionSort()
{
return make_shared<smtutil::FunctionSort>(
@@ -778,7 +821,12 @@ smtutil::SortPointer CHC::summarySort(FunctionDefinition const& _function, Contr
auto inputSorts = applyMap(_function.parameters(), smtSort);
auto outputSorts = applyMap(_function.returnParameters(), smtSort);
return make_shared<smtutil::FunctionSort>(
vector<smtutil::SortPointer>{smtutil::SortProvider::uintSort} + sorts + inputSorts + sorts + outputSorts,
vector<smtutil::SortPointer>{smtutil::SortProvider::uintSort} +
sorts +
inputSorts +
sorts +
inputSorts +
outputSorts,
smtutil::SortProvider::boolSort
);
}
@@ -802,11 +850,48 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
{
string suffix = base->name() + "_" + to_string(base->id());
m_interfaces[base] = createSymbolicBlock(interfaceSort(*base), "interface_" + suffix);
m_nondetInterfaces[base] = createSymbolicBlock(nondetInterfaceSort(*base), "nondet_interface_" + suffix);
for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*base))
if (!m_context.knownVariable(*var))
createVariable(*var);
/// Base nondeterministic interface that allows
/// 0 steps to be taken, used as base for the inductive
/// rule for each function.
auto const& iface = *m_nondetInterfaces.at(base);
auto state0 = stateVariablesAtIndex(0, *base);
addRule(iface(state0 + state0), "base_nondet");
for (auto const* function: base->definedFunctions())
{
for (auto var: function->parameters())
createVariable(*var);
for (auto var: function->returnParameters())
createVariable(*var);
for (auto const* var: function->localVariables())
createVariable(*var);
m_summaries[contract].emplace(function, createSummaryBlock(*function, *contract));
if (!base->isLibrary() && !base->isInterface() && !function->isConstructor())
{
auto state1 = stateVariablesAtIndex(1, *base);
auto state2 = stateVariablesAtIndex(2, *base);
auto nondetPre = iface(state0 + state1);
auto nondetPost = iface(state0 + state2);
vector<smtutil::Expression> args{m_error.currentValue()};
args += state1 +
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); }) +
state2 +
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) +
applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); });
connectBlocks(nondetPre, nondetPost, (*m_summaries.at(base).at(function))(args));
}
}
}
}
@@ -842,15 +927,22 @@ smtutil::Expression CHC::summary(ContractDefinition const&)
);
}
smtutil::Expression CHC::summary(FunctionDefinition const& _function)
smtutil::Expression CHC::summary(FunctionDefinition const& _function, ContractDefinition const& _contract)
{
vector<smtutil::Expression> args{m_error.currentValue()};
auto contract = _function.annotation().contract;
args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables();
args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables(_contract);
args += applyMap(_function.parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); });
args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables();
args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(_contract);
args += applyMap(_function.parameters(), [this](auto _var) { return currentValue(*_var); });
args += applyMap(_function.returnParameters(), [this](auto _var) { return currentValue(*_var); });
return (*m_summaries.at(m_currentContract).at(&_function))(args);
return (*m_summaries.at(&_contract).at(&_function))(args);
}
smtutil::Expression CHC::summary(FunctionDefinition const& _function)
{
solAssert(m_currentContract, "");
return summary(_function, *m_currentContract);
}
unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix)
@@ -893,13 +985,18 @@ vector<smtutil::Expression> CHC::initialStateVariables()
return stateVariablesAtIndex(0);
}
vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index)
vector<smtutil::Expression> CHC::initialStateVariables(ContractDefinition const& _contract)
{
solAssert(m_currentContract, "");
return applyMap(m_stateVariables, [&](auto _var) { return valueAtIndex(*_var, _index); });
return stateVariablesAtIndex(0, _contract);
}
vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract)
vector<smtutil::Expression> CHC::stateVariablesAtIndex(int _index)
{
solAssert(m_currentContract, "");
return stateVariablesAtIndex(_index, *m_currentContract);
}
vector<smtutil::Expression> CHC::stateVariablesAtIndex(int _index, ContractDefinition const& _contract)
{
return applyMap(
stateVariablesIncludingInheritedAndPrivate(_contract),
@@ -910,7 +1007,12 @@ vector<smtutil::Expression> CHC::stateVariablesAtIndex(unsigned _index, Contract
vector<smtutil::Expression> CHC::currentStateVariables()
{
solAssert(m_currentContract, "");
return applyMap(m_stateVariables, [this](auto _var) { return currentValue(*_var); });
return currentStateVariables(*m_currentContract);
}
vector<smtutil::Expression> CHC::currentStateVariables(ContractDefinition const& _contract)
{
return applyMap(stateVariablesIncludingInheritedAndPrivate(_contract), [this](auto _var) { return currentValue(*_var); });
}
vector<smtutil::Expression> CHC::currentFunctionVariables()
@@ -978,22 +1080,28 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
m_error.increaseIndex();
vector<smtutil::Expression> args{m_error.currentValue()};
auto const* contract = function->annotation().contract;
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
bool otherContract = contract->isLibrary() ||
funType.kind() == FunctionType::Kind::External ||
funType.kind() == FunctionType::Kind::BareStaticCall;
args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : currentStateVariables();
args += otherContract ? stateVariablesAtIndex(0, *contract) : currentStateVariables();
args += symbolicArguments(_funCall);
for (auto const& var: m_stateVariables)
m_context.variable(*var)->increaseIndex();
args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables();
if (!otherContract)
for (auto const& var: m_stateVariables)
m_context.variable(*var)->increaseIndex();
args += otherContract ? stateVariablesAtIndex(1, *contract) : currentStateVariables();
auto const& returnParams = function->returnParameters();
for (auto param: returnParams)
if (m_context.knownVariable(*param))
m_context.variable(*param)->increaseIndex();
for (auto var: function->parameters() + function->returnParameters())
{
if (m_context.knownVariable(*var))
m_context.variable(*var)->increaseIndex();
else
createVariable(*param);
args += applyMap(function->returnParameters(), [this](auto _var) { return currentValue(*_var); });
createVariable(*var);
args.push_back(currentValue(*var));
}
if (contract->isLibrary())
if (otherContract)
return (*m_summaries.at(contract).at(function))(args);
solAssert(m_currentContract, "");
+14 -3
View File
@@ -77,6 +77,7 @@ private:
void visitAssert(FunctionCall const& _funCall);
void internalFunctionCall(FunctionCall const& _funCall);
void externalFunctionCall(FunctionCall const& _funCall);
void unknownFunctionCall(FunctionCall const& _funCall);
void makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) override;
//@}
@@ -95,7 +96,6 @@ private:
void resetContractAnalysis();
void eraseKnowledge();
void clearIndices(ContractDefinition const* _contract, FunctionDefinition const* _function = nullptr) override;
bool shouldVisit(FunctionDefinition const& _function) const;
void setCurrentBlock(smt::SymbolicFunctionVariable const& _block, std::vector<smtutil::Expression> const* _arguments = nullptr);
std::set<Expression const*, IdCompare> transactionAssertions(ASTNode const* _txRoot);
static std::vector<VariableDeclaration const*> stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract);
@@ -106,7 +106,9 @@ private:
static std::vector<smtutil::SortPointer> stateSorts(ContractDefinition const& _contract);
smtutil::SortPointer constructorSort();
smtutil::SortPointer interfaceSort();
smtutil::SortPointer nondetInterfaceSort();
static smtutil::SortPointer interfaceSort(ContractDefinition const& _const);
static smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _const);
smtutil::SortPointer arity0FunctionSort();
smtutil::SortPointer sort(FunctionDefinition const& _function);
smtutil::SortPointer sort(ASTNode const* _block);
@@ -149,10 +151,12 @@ private:
/// @returns the symbolic values of the state variables at the beginning
/// of the current transaction.
std::vector<smtutil::Expression> initialStateVariables();
std::vector<smtutil::Expression> stateVariablesAtIndex(unsigned _index);
std::vector<smtutil::Expression> stateVariablesAtIndex(unsigned _index, ContractDefinition const& _contract);
std::vector<smtutil::Expression> initialStateVariables(ContractDefinition const& _contract);
std::vector<smtutil::Expression> stateVariablesAtIndex(int _index);
std::vector<smtutil::Expression> stateVariablesAtIndex(int _index, ContractDefinition const& _contract);
/// @returns the current symbolic values of the current state variables.
std::vector<smtutil::Expression> currentStateVariables();
std::vector<smtutil::Expression> currentStateVariables(ContractDefinition const& _contract);
/// @returns the current symbolic values of the current function's
/// input and output parameters.
@@ -173,6 +177,7 @@ private:
smtutil::Expression summary(ContractDefinition const& _contract);
/// @returns a predicate that defines a function summary.
smtutil::Expression summary(FunctionDefinition const& _function);
smtutil::Expression summary(FunctionDefinition const& _function, ContractDefinition const& _contract);
//@}
/// Solver related.
@@ -212,6 +217,12 @@ private:
/// Single entry block for all functions.
std::map<ContractDefinition const*, std::unique_ptr<smt::SymbolicFunctionVariable>> m_interfaces;
/// Nondeterministic interfaces.
/// These are used when the analyzed contract makes external calls to unknown code,
/// which means that the analyzed contract can potentially be called
/// nondeterministically.
std::map<ContractDefinition const*, std::unique_ptr<smt::SymbolicFunctionVariable>> m_nondetInterfaces;
/// Artificial Error predicate.
/// Single error block for all assertions.
std::unique_ptr<smt::SymbolicFunctionVariable> m_errorPredicate;
+6 -19
View File
@@ -94,19 +94,12 @@ void DocStringParser::parse(string const& _docString, ErrorReporter& _errorRepor
{
// we found a tag
auto tagNameEndPos = firstWhitespaceOrNewline(tagPos, end);
if (tagNameEndPos == end)
{
m_errorReporter->docstringParsingError(
9222_error,
"End of tag " + string(tagPos, tagNameEndPos) + " not found"
);
break;
}
currPos = parseDocTag(tagNameEndPos + 1, end, string(tagPos + 1, tagNameEndPos));
auto tagName = string(tagPos + 1, tagNameEndPos);
auto tagDataPos = (tagNameEndPos != end) ? tagNameEndPos + 1 : tagNameEndPos;
currPos = parseDocTag(tagDataPos, end, tagName);
}
else if (!!m_lastTag) // continuation of the previous tag
currPos = appendDocTag(currPos, end);
currPos = parseDocTagLine(currPos, end, true);
else if (currPos != end)
{
// if it begins without a tag then consider it as @notice
@@ -127,7 +120,7 @@ DocStringParser::iter DocStringParser::parseDocTagLine(iter _pos, iter _end, boo
{
solAssert(!!m_lastTag, "");
auto nlPos = find(_pos, _end, '\n');
if (_appending && _pos < _end && *_pos != ' ' && *_pos != '\t')
if (_appending && _pos != _end && *_pos != ' ' && *_pos != '\t')
m_lastTag->content += " ";
else if (!_appending)
_pos = skipWhitespace(_pos, _end);
@@ -179,13 +172,7 @@ DocStringParser::iter DocStringParser::parseDocTag(iter _pos, iter _end, string
}
}
else
return appendDocTag(_pos, _end);
}
DocStringParser::iter DocStringParser::appendDocTag(iter _pos, iter _end)
{
solAssert(!!m_lastTag, "");
return parseDocTagLine(_pos, _end, true);
return parseDocTagLine(_pos, _end, true);
}
void DocStringParser::newTag(string const& _tagName)
-1
View File
@@ -50,7 +50,6 @@ private:
iter parseDocTagParam(iter _pos, iter _end);
iter appendDocTagParam(iter _pos, iter _end);
void parseDocString(std::string const& _string);
iter appendDocTag(iter _pos, iter _end);
/// Parses the doc tag named @a _tag, adds it to m_docTags and returns the position
/// after the tag.
iter parseDocTag(iter _pos, iter _end, std::string const& _tag);