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

This commit is contained in:
chriseth
2020-11-23 19:28:08 +01:00
511 changed files with 1263 additions and 372 deletions
+41 -3
View File
@@ -198,11 +198,11 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition:
});
}
uint64_t ContractDefinition::interfaceId() const
uint32_t ContractDefinition::interfaceId() const
{
uint64_t result{0};
uint32_t result{0};
for (auto const& function: interfaceFunctionList(false))
result ^= util::fromBigEndian<uint64_t>(function.first.ref());
result ^= util::fromBigEndian<uint32_t>(function.first.ref());
return result;
}
@@ -752,6 +752,44 @@ FunctionCallAnnotation& FunctionCall::annotation() const
return initAnnotation<FunctionCallAnnotation>();
}
vector<ASTPointer<Expression const>> FunctionCall::sortedArguments() const
{
// normal arguments
if (m_names.empty())
return arguments();
// named arguments
FunctionTypePointer functionType;
if (*annotation().kind == FunctionCallKind::StructConstructorCall)
{
auto const& type = dynamic_cast<TypeType const&>(*m_expression->annotation().type);
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
functionType = structType.constructorType();
}
else
functionType = dynamic_cast<FunctionType const*>(m_expression->annotation().type);
vector<ASTPointer<Expression const>> sorted;
for (auto const& parameterName: functionType->parameterNames())
{
bool found = false;
for (size_t j = 0; j < m_names.size() && !found; j++)
if ((found = (parameterName == *m_names.at(j))))
// we found the actual parameter position
sorted.push_back(m_arguments.at(j));
solAssert(found, "");
}
if (!functionType->takesArbitraryParameters())
{
solAssert(m_arguments.size() == functionType->parameterTypes().size(), "");
solAssert(m_arguments.size() == m_names.size(), "");
solAssert(m_arguments.size() == sorted.size(), "");
}
return sorted;
}
IdentifierAnnotation& Identifier::annotation() const
{
return initAnnotation<IdentifierAnnotation>();
+7 -1
View File
@@ -515,7 +515,7 @@ public:
std::map<util::FixedHash<4>, FunctionTypePointer> interfaceFunctions(bool _includeInheritedFunctions = true) const;
std::vector<std::pair<util::FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList(bool _includeInheritedFunctions = true) const;
/// @returns the EIP-165 compatible interface identifier. This will exclude inherited functions.
uint64_t interfaceId() const;
uint32_t interfaceId() const;
/// @returns a list of all declarations in this contract
std::vector<Declaration const*> declarations() const { return filteredNodes<Declaration>(m_subNodes); }
@@ -1936,7 +1936,13 @@ public:
void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
/// @returns the given arguments in the order they were written.
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
/// @returns the given arguments sorted by how the called function takes them.
std::vector<ASTPointer<Expression const>> sortedArguments() const;
/// @returns the list of given argument names if this is a named call,
/// in the order they were written.
/// If this is not a named call, this is empty.
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
FunctionCallAnnotation& annotation() const override;
+6 -2
View File
@@ -3002,6 +3002,11 @@ TypePointers FunctionType::parameterTypes() const
return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend());
}
TypePointers const& FunctionType::parameterTypesIncludingSelf() const
{
return m_parameterTypes;
}
string FunctionType::richIdentifier() const
{
string id = "t_function_";
@@ -3247,8 +3252,7 @@ vector<tuple<string, TypePointer>> FunctionType::makeStackItems() const
if (m_saltSet)
slots.emplace_back("salt", TypeProvider::fixedBytes(32));
if (bound())
for (auto const& [boundName, boundType]: m_parameterTypes.front()->stackItems())
slots.emplace_back("self_" + boundName, boundType);
slots.emplace_back("self", m_parameterTypes.front());
return slots;
}
+1
View File
@@ -1239,6 +1239,7 @@ public:
static FunctionTypePointer newExpressionType(ContractDefinition const& _contract);
TypePointers parameterTypes() const;
TypePointers const& parameterTypesIncludingSelf() const;
std::vector<std::string> parameterNames() const;
TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; }
/// @returns the list of return parameter types. All dynamically-sized types (this excludes
+54 -40
View File
@@ -42,6 +42,7 @@ string ABIFunctions::tupleEncoder(
bool _reversed
)
{
solAssert(_givenTypes.size() == _targetTypes.size(), "");
EncodingOptions options;
options.encodeAsLibraryTypes = _encodeAsLibraryTypes;
options.encodeFunctionFromStack = true;
@@ -1078,8 +1079,6 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo
solAssert(!_fromMemory, "");
return abiDecodingFunctionCalldataArray(*arrayType);
}
else if (arrayType->isByteArray())
return abiDecodingFunctionByteArray(*arrayType, _fromMemory);
else
return abiDecodingFunctionArray(*arrayType, _fromMemory);
}
@@ -1132,36 +1131,21 @@ string ABIFunctions::abiDecodingFunctionValueType(Type const& _type, bool _fromM
string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory)
{
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
solAssert(!_type.isByteArray(), "");
string functionName =
"abi_decode_" +
_type.identifier() +
(_fromMemory ? "_fromMemory" : "");
solAssert(!_type.dataStoredIn(DataLocation::Storage), "");
return createFunction(functionName, [&]() {
string load = _fromMemory ? "mload" : "calldataload";
bool dynamicBase = _type.baseType()->isDynamicallyEncoded();
Whiskers templ(
R"(
// <readableTypeName>
function <functionName>(offset, end) -> array {
if iszero(slt(add(offset, 0x1f), end)) { <revertString> }
let length := <retrieveLength>
array := <allocate>(<allocationSize>(length))
let dst := array
<storeLength> // might update offset and dst
let src := offset
<staticBoundsCheck>
for { let i := 0 } lt(i, length) { i := add(i, 1) }
{
let elementPos := <retrieveElementPos>
mstore(dst, <decodingFun>(elementPos, end))
dst := add(dst, 0x20)
src := add(src, <stride>)
}
array := <abiDecodeAvailableLen>(<offset>, length, end)
}
)"
);
@@ -1169,18 +1153,56 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
templ("revertString", revertReasonIfDebug("ABI decoding: invalid calldata array offset"));
templ("functionName", functionName);
templ("readableTypeName", _type.toString(true));
templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)");
templ("retrieveLength", _type.isDynamicallySized() ? (load + "(offset)") : toCompactHexWithPrefix(_type.length()));
templ("offset", _type.isDynamicallySized() ? "add(offset, 0x20)" : "offset");
templ("abiDecodeAvailableLen", abiDecodingFunctionArrayAvailableLength(_type, _fromMemory));
return templ.render();
});
}
string ABIFunctions::abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
{
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
if (_type.isByteArray())
return abiDecodingFunctionByteArrayAvailableLength(_type, _fromMemory);
string functionName =
"abi_decode_available_length_" +
_type.identifier() +
(_fromMemory ? "_fromMemory" : "");
return createFunction(functionName, [&]() {
Whiskers templ(R"(
// <readableTypeName>
function <functionName>(offset, length, end) -> array {
array := <allocate>(<allocationSize>(length))
let dst := array
<storeLength>
let src := offset
<staticBoundsCheck>
for { let i := 0 } lt(i, length) { i := add(i, 1) }
{
let elementPos := <retrieveElementPos>
mstore(dst, <decodingFun>(elementPos, end))
dst := add(dst, 0x20)
src := add(src, <stride>)
}
}
)");
templ("functionName", functionName);
templ("readableTypeName", _type.toString(true));
templ("allocate", m_utils.allocationFunction());
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
string calldataStride = toCompactHexWithPrefix(_type.calldataStride());
templ("stride", calldataStride);
if (_type.isDynamicallySized())
templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)");
templ("storeLength", "mstore(array, length) dst := add(array, 0x20)");
else
templ("storeLength", "");
if (dynamicBase)
if (_type.baseType()->isDynamicallyEncoded())
{
templ("staticBoundsCheck", "");
string load = _fromMemory ? "mload" : "calldataload";
templ("retrieveElementPos", "add(offset, " + load + "(src))");
}
else
@@ -1244,36 +1266,28 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
});
}
string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory)
string ABIFunctions::abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory)
{
solAssert(_type.dataStoredIn(DataLocation::Memory), "");
solAssert(_type.isByteArray(), "");
string functionName =
"abi_decode_" +
"abi_decode_available_length_" +
_type.identifier() +
(_fromMemory ? "_fromMemory" : "");
return createFunction(functionName, [&]() {
Whiskers templ(
R"(
function <functionName>(offset, end) -> array {
if iszero(slt(add(offset, 0x1f), end)) { <revertStringOffset> }
let length := <load>(offset)
array := <allocate>(<allocationSize>(length))
mstore(array, length)
let src := add(offset, 0x20)
let dst := add(array, 0x20)
if gt(add(src, length), end) { <revertStringLength> }
<copyToMemFun>(src, dst, length)
}
)"
);
// TODO add test
templ("revertStringOffset", revertReasonIfDebug("ABI decoding: invalid byte array offset"));
Whiskers templ(R"(
function <functionName>(src, length, end) -> array {
array := <allocate>(<allocationSize>(length))
mstore(array, length)
let dst := add(array, 0x20)
if gt(add(src, length), end) { <revertStringLength> }
<copyToMemFun>(src, dst, length)
}
)");
templ("revertStringLength", revertReasonIfDebug("ABI decoding: invalid byte array length"));
templ("functionName", functionName);
templ("load", _fromMemory ? "mload" : "calldataload");
templ("allocate", m_utils.allocationFunction());
templ("allocationSize", m_utils.arrayAllocationSizeFunction(_type));
templ("copyToMemFun", m_utils.copyToMemoryFunction(!_fromMemory));
+9 -5
View File
@@ -165,6 +165,11 @@ public:
EncodingOptions const& _options
);
/// Decodes array in case of dynamic arrays with offset pointing to
/// data and length already on stack
/// signature: (dataOffset, length, dataEnd) -> decodedArray
std::string abiDecodingFunctionArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
private:
/// Part of @a abiEncodingFunction for array target type and given calldata array.
/// Uses calldatacopy and does not perform cleanup or validation and can therefore only
@@ -234,15 +239,14 @@ private:
std::string abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for calldata array types.
std::string abiDecodingFunctionCalldataArray(ArrayType const& _type);
/// Part of @a abiDecodingFunction for byte array types.
std::string abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunctionArrayWithAvailableLength
std::string abiDecodingFunctionByteArrayAvailableLength(ArrayType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for calldata struct types.
std::string abiDecodingFunctionCalldataStruct(StructType const& _type);
/// Part of @a abiDecodingFunction for struct types.
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
/// Part of @a abiDecodingFunction for array types.
std::string abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack);
/// Part of @a abiDecodingFunction for struct types.
std::string abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory);
/// @returns the name of a function that retrieves an element from calldata.
std::string calldataAccessFunction(Type const& _type);
+1 -19
View File
@@ -558,26 +558,8 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
TypePointers parameterTypes = functionType->parameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
if (!functionType->takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: functionType->parameterNames())
{
bool found = false;
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
if ((found = (parameterName == *callArgumentNames[j])))
// we found the actual parameter position
arguments.push_back(callArguments[j]);
solAssert(found, "");
}
vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
if (functionCallKind == FunctionCallKind::StructConstructorCall)
{
+26 -13
View File
@@ -3170,6 +3170,12 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
break;
}
case Type::Category::Mapping:
{
solAssert(_from == _to, "");
body = "converted := value";
break;
}
default:
solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
}
@@ -3182,7 +3188,8 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayType const& _to)
{
solUnimplementedAssert(_to.location() != DataLocation::CallData, "Conversion of calldata types not yet implemented.");
solAssert(_to.location() != DataLocation::CallData, "");
// Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
if (_to.location() == DataLocation::Storage)
solAssert(
@@ -3190,12 +3197,6 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy
_from.location() == DataLocation::Storage,
"Invalid conversion to storage type."
);
if (_to.location() == DataLocation::Memory && _from.location() == DataLocation::CallData)
{
solUnimplementedAssert(_from.isDynamicallySized(), "");
solUnimplementedAssert(!_from.baseType()->isDynamicallyEncoded(), "");
solUnimplementedAssert(_from.isByteArray() && _to.isByteArray() && _to.isDynamicallySized(), "");
}
string functionName =
"convert_array_" +
@@ -3223,19 +3224,31 @@ string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayTy
"body",
Whiskers(R"(
// Copy the array to a free position in memory
converted :=
<?fromStorage>
converted := <arrayStorageToMem>(value)
<arrayStorageToMem>(value)
</fromStorage>
<?fromCalldata>
converted := <allocateMemoryArray>(length)
<copyToMemory>(value, add(converted, 0x20), length)
<abiDecode>(value, <length>, calldatasize())
</fromCalldata>
)")
("fromStorage", _from.dataStoredIn(DataLocation::Storage))
("fromCalldata", _from.dataStoredIn(DataLocation::CallData))
("allocateMemoryArray", _from.dataStoredIn(DataLocation::CallData) ? allocateMemoryArrayFunction(_to) : "")
("copyToMemory", _from.dataStoredIn(DataLocation::CallData) ? copyToMemoryFunction(true) : "")
("arrayStorageToMem", _from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : "")
("length", _from.isDynamicallySized() ? "length" : _from.length().str())
(
"abiDecode",
_from.dataStoredIn(DataLocation::CallData) ?
ABIFunctions(
m_evmVersion,
m_revertStrings,
m_functionCollector
).abiDecodingFunctionArrayAvailableLength(_to, false) :
""
)
(
"arrayStorageToMem",
_from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : ""
)
.render()
);
else
+1 -1
View File
@@ -28,7 +28,7 @@ using namespace solidity::frontend;
YulArity YulArity::fromType(FunctionType const& _functionType)
{
return YulArity{
TupleType(_functionType.parameterTypes()).sizeOnStack(),
TupleType(_functionType.parameterTypesIncludingSelf()).sizeOnStack(),
TupleType(_functionType.returnParameterTypes()).sizeOnStack()
};
}
@@ -241,6 +241,10 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
return;
_varDecl.value()->accept(*this);
Type const* rightIntermediateType = _varDecl.value()->annotation().type->closestTemporaryType(_varDecl.type());
solAssert(rightIntermediateType, "");
IRVariable value = convert(*_varDecl.value(), *rightIntermediateType);
writeToLValue(
_varDecl.immutable() ?
IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} :
@@ -248,7 +252,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va
util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first),
m_context.storageLocationOfStateVariable(_varDecl).second
}},
*_varDecl.value()
value
);
}
catch (langutil::UnimplementedFeatureError const& _error)
@@ -826,7 +830,6 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
if (
functionType &&
functionType->kind() == FunctionType::Kind::Internal &&
!functionType->bound() &&
IRHelpers::referencedFunctionDeclaration(_functionCall.expression())
)
m_context.internalFunctionCalledDirectly(_functionCall.expression());
@@ -861,26 +864,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type);
TypePointers parameterTypes = functionType->parameterTypes();
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.names();
if (!functionType->takesArbitraryParameters())
solAssert(callArguments.size() == parameterTypes.size(), "");
vector<ASTPointer<Expression const>> arguments;
if (callArgumentNames.empty())
// normal arguments
arguments = callArguments;
else
// named arguments
for (auto const& parameterName: functionType->parameterNames())
{
auto const it = std::find_if(callArgumentNames.cbegin(), callArgumentNames.cend(), [&](ASTPointer<ASTString> const& _argName) {
return *_argName == parameterName;
});
solAssert(it != callArgumentNames.cend(), "");
arguments.push_back(callArguments[static_cast<size_t>(std::distance(callArgumentNames.begin(), it))]);
}
vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments();
if (functionCallKind == FunctionCallKind::StructConstructorCall)
{
@@ -921,8 +906,6 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
solAssert(functionType->kind() == FunctionType::Kind::Internal || functionType->kind() == FunctionType::Kind::DelegateCall, "");
}
}
else
solAssert(!functionType->bound(), "");
switch (functionType->kind())
{
@@ -957,19 +940,17 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
solAssert(functionDef && functionDef->isImplemented(), "");
solAssert(
functionDef->parameters().size() == arguments.size() + (functionType->bound() ? 1 : 0),
""
);
}
solAssert(!functionType->takesArbitraryParameters(), "");
vector<string> args;
if (functionType->bound())
{
solAssert(memberAccess && functionDef, "");
solAssert(functionDef->parameters().size() == arguments.size() + 1, "");
args += convert(memberAccess->expression(), *functionDef->parameters()[0]->type()).stackSlots();
}
else
solAssert(!functionDef || functionDef->parameters().size() == arguments.size(), "");
args += IRVariable(_functionCall.expression()).part("self").stackSlots();
for (size_t i = 0; i < arguments.size(); ++i)
args += convert(*arguments[i], *parameterTypes[i]).stackSlots();
@@ -1585,6 +1566,23 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
Type::Category::Array,
Type::Category::FixedBytes,
}).count(objectCategory) > 0, "");
define(IRVariable(_memberAccess).part("self"), _memberAccess.expression());
auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration());
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
if (memberFunctionType->kind() == FunctionType::Kind::Internal)
{
define(IRVariable(_memberAccess).part("functionIdentifier")) << to_string(functionDefinition.id()) << "\n";
m_context.internalFunctionAccessed(_memberAccess, functionDefinition);
}
else
{
solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall, "");
auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope());
solAssert(contract && contract->isLibrary(), "");
define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n";
define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier();
}
return;
}
@@ -2294,15 +2292,21 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
"Can only be used for regular external calls."
);
solUnimplementedAssert(!funType.bound(), "");
bool const isDelegateCall = funKind == FunctionType::Kind::DelegateCall;
bool const useStaticCall = funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
TypePointers parameterTypes = funType.parameterTypes();
TypePointers argumentTypes;
vector<string> argumentStrings;
if (funType.bound())
{
parameterTypes.insert(parameterTypes.begin(), funType.selfType());
argumentTypes.emplace_back(funType.selfType());
argumentStrings += IRVariable(_functionCall.expression()).part("self").stackSlots();
}
for (auto const& arg: _arguments)
{
argumentTypes.emplace_back(&type(*arg));
@@ -2381,7 +2385,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall;
solAssert(funType.padArguments(), "");
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, funType.parameterTypes(), encodeForLibraryCall));
templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall));
templ("argumentString", joinHumanReadablePrefixed(argumentStrings));
solAssert(!isDelegateCall || !funType.valueSet(), "Value set for delegatecall");
+23 -16
View File
@@ -622,11 +622,7 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall)
createExpr(_funCall);
if (functionCallKind == FunctionCallKind::StructConstructorCall)
{
m_errorReporter.warning(
4639_error,
_funCall.location(),
"Assertion checker does not yet implement this expression."
);
visitStructConstructorCall(_funCall);
return;
}
@@ -856,6 +852,9 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
defineExpr(_identifier, m_context.state().thisAddress());
m_uninterpretedTerms.insert(&_identifier);
}
// Ignore type identifiers
else if (dynamic_cast<TypeType const*>(_identifier.annotation().type))
return;
// Ignore the builtin abi, it is handled in FunctionCall.
// TODO: ignore MagicType in general (abi, block, msg, tx, type)
else if (auto magicType = dynamic_cast<MagicType const*>(_identifier.annotation().type); magicType && magicType->kind() == MagicType::Kind::ABI)
@@ -1013,6 +1012,13 @@ void SMTEncoder::visitFunctionIdentifier(Identifier const& _identifier)
}
}
void SMTEncoder::visitStructConstructorCall(FunctionCall const& _funCall)
{
solAssert(*_funCall.annotation().kind == FunctionCallKind::StructConstructorCall, "");
auto& structSymbolicVar = dynamic_cast<smt::SymbolicStructVariable&>(*m_context.expression(_funCall));
structSymbolicVar.assignAllMembers(applyMap(_funCall.sortedArguments(), [this](auto const& arg) { return expr(*arg); }));
}
void SMTEncoder::endVisit(Literal const& _literal)
{
solAssert(_literal.annotation().type, "Expected type for AST node");
@@ -1073,10 +1079,10 @@ void SMTEncoder::endVisit(Return const& _return)
solAssert(types.size() == returnParams.size(), "");
for (unsigned i = 0; i < returnParams.size(); ++i)
m_context.addAssertion(symbTuple->component(i, types.at(i), returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i)));
assignment(*returnParams.at(i), symbTuple->component(i, types.at(i), returnParams.at(i)->type()));
}
else if (returnParams.size() == 1)
m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
assignment(*returnParams.front(), expr(*_return.expression(), returnParams.front()->type()));
}
}
@@ -1292,8 +1298,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
if (var->hasReferenceOrMappingType())
resetReferences(*var);
m_context.addAssertion(m_context.newValue(*var) == _rightHandSide);
m_context.expression(_expr)->increaseIndex();
assignment(*var, _rightHandSide);
defineExpr(_expr, currentValue(*var));
return;
}
@@ -1318,7 +1323,6 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
smtutil::Expression(make_shared<smtutil::SortSort>(smt::smtSort(*baseType)), baseType->toString(true)),
{smtutil::Expression::store(symbArray->elements(), indexExpr, toStore), symbArray->length()}
);
m_context.expression(*indexAccess)->increaseIndex();
defineExpr(*indexAccess, smtutil::Expression::select(
symbArray->elements(),
indexExpr
@@ -1359,8 +1363,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
if (varDecl->hasReferenceOrMappingType())
resetReferences(*varDecl);
m_context.addAssertion(m_context.newValue(*varDecl) == toStore);
m_context.expression(*id)->increaseIndex();
assignment(*varDecl, toStore);
defineExpr(*id, currentValue(*varDecl));
break;
}
@@ -1373,8 +1376,7 @@ void SMTEncoder::indexOrMemberAssignment(Expression const& _expr, smtutil::Expre
)
resetReferences(type);
m_context.expression(*lastExpr)->increaseIndex();
m_context.addAssertion(expr(*lastExpr) == toStore);
assignment(*m_context.expression(*lastExpr), toStore);
break;
}
}
@@ -1953,7 +1955,12 @@ void SMTEncoder::assignment(VariableDeclaration const& _variable, smtutil::Expre
TypePointer type = _variable.type();
if (type->category() == Type::Category::Mapping)
arrayAssignment();
m_context.addAssertion(m_context.newValue(_variable) == _value);
assignment(*m_context.variable(_variable), _value);
}
void SMTEncoder::assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value)
{
m_context.addAssertion(_symVar.increaseIndex() == _value);
}
SMTEncoder::VariableIndices SMTEncoder::visitBranch(ASTNode const* _statement, smtutil::Expression _condition)
@@ -2525,8 +2532,8 @@ vector<smtutil::Expression> SMTEncoder::symbolicArguments(FunctionCall const& _f
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
solAssert(funType, "");
vector<ASTPointer<Expression const>> arguments = _funCall.sortedArguments();
auto const& functionParams = function->parameters();
auto const& arguments = _funCall.arguments();
unsigned firstParam = 0;
if (funType->bound())
{
+5
View File
@@ -151,6 +151,7 @@ protected:
virtual void visitAddMulMod(FunctionCall const& _funCall);
void visitObjectCreation(FunctionCall const& _funCall);
void visitTypeConversion(FunctionCall const& _funCall);
void visitStructConstructorCall(FunctionCall const& _funCall);
void visitFunctionIdentifier(Identifier const& _identifier);
/// Encodes a modifier or function body according to the modifier
@@ -194,6 +195,10 @@ protected:
IntegerType const& _type
);
/// Handles the actual assertion of the new value to the encoding context.
/// Other assignment methods should use this one in the end.
void assignment(smt::SymbolicVariable& _symVar, smtutil::Expression const& _value);
void assignment(VariableDeclaration const& _variable, Expression const& _value);
/// Handles assignments to variables of different types.
void assignment(VariableDeclaration const& _variable, smtutil::Expression const& _value);
+16 -1
View File
@@ -360,7 +360,7 @@ SymbolicStructVariable::SymbolicStructVariable(
}
}
smtutil::Expression SymbolicStructVariable::member(string const& _member)
smtutil::Expression SymbolicStructVariable::member(string const& _member) const
{
return smtutil::Expression::tuple_get(currentValue(), m_memberIndices.at(_member));
}
@@ -385,3 +385,18 @@ smtutil::Expression SymbolicStructVariable::assignMember(string const& _member,
return currentValue();
}
smtutil::Expression SymbolicStructVariable::assignAllMembers(vector<smtutil::Expression> const& _memberValues)
{
auto structType = dynamic_cast<StructType const*>(m_type);
solAssert(structType, "");
auto const& structDef = structType->structDefinition();
auto const& structMembers = structDef.members();
solAssert(_memberValues.size() == structMembers.size(), "");
increaseIndex();
for (unsigned i = 0; i < _memberValues.size(); ++i)
m_context.addAssertion(_memberValues[i] == member(structMembers[i]->name()));
return currentValue();
}
+5 -1
View File
@@ -282,12 +282,16 @@ public:
);
/// @returns the symbolic expression representing _member.
smtutil::Expression member(std::string const& _member);
smtutil::Expression member(std::string const& _member) const;
/// @returns the symbolic expression representing this struct
/// with field _member updated.
smtutil::Expression assignMember(std::string const& _member, smtutil::Expression const& _memberValue);
/// @returns the symbolic expression representing this struct
/// with all fields updated with the given values.
smtutil::Expression assignAllMembers(std::vector<smtutil::Expression> const& _memberValues);
private:
std::map<std::string, unsigned> m_memberIndices;
};