From 14fd647b852146cd885426d04bffffbd1b7c08a0 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 9 Nov 2017 00:02:39 -0300 Subject: Fix event parsing. Refs #3175 --- libsolidity/parsing/Parser.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 821e81d2..05b877b5 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -644,15 +644,11 @@ ASTPointer Parser::parseEventDefinition() expectToken(Token::Event); ASTPointer name(expectIdentifierToken()); - ASTPointer parameters; - if (m_scanner->currentToken() == Token::LParen) - { - VarDeclParserOptions options; - options.allowIndexed = true; - parameters = parseParameterList(options); - } - else - parameters = createEmptyParameterList(); + + VarDeclParserOptions options; + options.allowIndexed = true; + ASTPointer parameters = parseParameterList(options); + bool anonymous = false; if (m_scanner->currentToken() == Token::Anonymous) { -- cgit v1.2.3 From bdc1ff8ec71bdf96e0706c951dd92def3aee4aa2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Sep 2017 13:59:21 +0200 Subject: ABI decoder. --- libsolidity/codegen/ABIFunctions.cpp | 425 ++++++++++++++++++++++++++++++- libsolidity/codegen/ABIFunctions.h | 47 ++++ libsolidity/codegen/CompilerContext.cpp | 1 + libsolidity/codegen/CompilerUtils.cpp | 17 ++ libsolidity/codegen/CompilerUtils.h | 7 + libsolidity/codegen/ContractCompiler.cpp | 10 + 6 files changed, 503 insertions(+), 4 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index bb39cbbb..c9a9ff57 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -22,9 +22,13 @@ #include +#include +#include + #include -#include +#include +#include using namespace std; using namespace dev; @@ -99,6 +103,73 @@ string ABIFunctions::tupleEncoder( }); } +string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) +{ + string functionName = string("abi_decode_tuple_"); + for (auto const& t: _types) + functionName += t->identifier(); + if (_fromMemory) + functionName += "_fromMemory"; + + return createFunction(functionName, [&]() { + solAssert(!_types.empty(), ""); + + TypePointers decodingTypes; + for (auto const& t: _types) + decodingTypes.emplace_back(t->decodingType()); + + Whiskers templ(R"( + function (headStart, dataEnd) -> { + switch slt(sub(dataEnd, headStart), ) case 1 { revert(0, 0) } + + } + )"); + templ("functionName", functionName); + templ("minimumSize", to_string(headSize(decodingTypes))); + + string decodeElements; + vector valueReturnParams; + size_t headPos = 0; + size_t stackPos = 0; + for (size_t i = 0; i < _types.size(); ++i) + { + solAssert(_types[i], ""); + solAssert(decodingTypes[i], ""); + size_t sizeOnStack = _types[i]->sizeOnStack(); + solAssert(sizeOnStack == decodingTypes[i]->sizeOnStack(), ""); + solAssert(sizeOnStack > 0, ""); + vector valueNamesLocal; + for (size_t j = 0; j < sizeOnStack; j++) + { + valueNamesLocal.push_back("value" + to_string(stackPos)); + valueReturnParams.push_back("value" + to_string(stackPos)); + stackPos++; + } + bool dynamic = decodingTypes[i]->isDynamicallyEncoded(); + Whiskers elementTempl(R"( + { + let offset := )" + string( + dynamic ? + "(add(headStart, ))" : + "" + ) + R"( + := (add(headStart, offset), dataEnd) + } + )"); + elementTempl("load", _fromMemory ? "mload" : "calldataload"); + elementTempl("values", boost::algorithm::join(valueNamesLocal, ", ")); + elementTempl("pos", to_string(headPos)); + elementTempl("abiDecode", abiDecodingFunction(*_types[i], _fromMemory, true)); + decodeElements += elementTempl.render(); + headPos += dynamic ? 0x20 : decodingTypes[i]->calldataEncodedSize(); + } + templ("valueReturnParams", boost::algorithm::join(valueReturnParams, ", ")); + templ("decodeElements", decodeElements); + + return templ.render(); + }); +} + string ABIFunctions::requestedFunctions() { string result; @@ -141,10 +212,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure) solUnimplemented("Fixed point types not implemented."); break; case Type::Category::Array: - solAssert(false, "Array cleanup requested."); - break; case Type::Category::Struct: - solAssert(false, "Struct cleanup requested."); + solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type."); + templ("body", "cleaned := value"); break; case Type::Category::FixedBytes: { @@ -367,6 +437,24 @@ string ABIFunctions::combineExternalFunctionIdFunction() }); } +string ABIFunctions::splitExternalFunctionIdFunction() +{ + string functionName = "split_external_function_id"; + return createFunction(functionName, [&]() { + return Whiskers(R"( + function (combined) -> addr, selector { + combined := (combined) + selector := and(combined, 0xffffffff) + addr := (combined) + } + )") + ("functionName", functionName) + ("shr32", shiftRightFunction(32, false)) + ("shr64", shiftRightFunction(64, false)) + .render(); + }); +} + string ABIFunctions::abiEncodingFunction( Type const& _from, Type const& _to, @@ -963,6 +1051,290 @@ string ABIFunctions::abiEncodingFunctionFunctionType( }); } +string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bool _forUseOnStack) +{ + TypePointer decodingType = _type.decodingType(); + solAssert(decodingType, ""); + + if (auto arrayType = dynamic_cast(decodingType.get())) + { + if (arrayType->dataStoredIn(DataLocation::CallData)) + { + solAssert(!_fromMemory, ""); + return abiDecodingFunctionCalldataArray(*arrayType); + } + else if (arrayType->isByteArray()) + return abiDecodingFunctionByteArray(*arrayType, _fromMemory); + else + return abiDecodingFunctionArray(*arrayType, _fromMemory); + } + else if (auto const* structType = dynamic_cast(decodingType.get())) + return abiDecodingFunctionStruct(*structType, _fromMemory); + else if (auto const* functionType = dynamic_cast(decodingType.get())) + return abiDecodingFunctionFunctionType(*functionType, _fromMemory, _forUseOnStack); + + solAssert(decodingType->sizeOnStack() == 1, ""); + solAssert(decodingType->isValueType(), ""); + solAssert(decodingType->calldataEncodedSize() == 32, ""); + solAssert(!decodingType->isDynamicallyEncoded(), ""); + + string functionName = + "abi_decode_" + + _type.identifier() + + (_fromMemory ? "_fromMemory" : ""); + return createFunction(functionName, [&]() { + Whiskers templ(R"( + function (offset, end) -> value { + value := ((offset)) + } + )"); + templ("functionName", functionName); + templ("load", _fromMemory ? "mload" : "calldataload"); + // Cleanup itself should use the type and not decodingType, because e.g. + // the decoding type of an enum is a plain int. + templ("cleanup", cleanupFunction(_type, true)); + return templ.render(); + }); +} + +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"( + // + function (offset, end) -> array { + let length := + array := ((length)) + let dst := array + // might update offset and dst + let src := offset + + for { let i := 0 } lt(i, length) { i := add(i, 1) } + { + let elementPos := + + mstore(dst, (elementPos, end)) + dst := add(dst, 0x20) + src := add(src, ) + } + } + )" + ); + templ("functionName", functionName); + templ("readableTypeName", _type.toString(true)); + templ("retrieveLength", !_type.isDynamicallySized() ? toCompactHexWithPrefix(_type.length()) : load + "(offset)"); + templ("allocate", allocationFunction()); + templ("allocationSize", arrayAllocationSizeFunction(_type)); + if (_type.isDynamicallySized()) + templ("storeLength", "mstore(array, length) offset := add(offset, 0x20) dst := add(dst, 0x20)"); + else + templ("storeLength", ""); + if (dynamicBase) + { + templ("staticBoundsCheck", ""); + // The dynamic bounds check might not be needed (because we have an additional check + // one level deeper), but we keep it in just in case. This at least prevents + // the part one level deeper from reading the length from an out of bounds position. + templ("dynamicBoundsCheck", "switch gt(elementPos, end) case 1 { revert(0, 0) }"); + templ("retrieveElementPos", "add(offset, " + load + "(src))"); + templ("baseEncodedSize", "0x20"); + } + else + { + string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize()); + templ("staticBoundsCheck", "switch gt(add(src, mul(length, " + baseEncodedSize + ")), end) case 1 { revert(0, 0) }"); + templ("dynamicBoundsCheck", ""); + templ("retrieveElementPos", "src"); + templ("baseEncodedSize", baseEncodedSize); + } + templ("decodingFun", abiDecodingFunction(*_type.baseType(), _fromMemory, false)); + return templ.render(); + }); +} + +string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) +{ + // This does not work with arrays of complex types - the array access + // is not yet implemented in Solidity. + solAssert(_type.dataStoredIn(DataLocation::CallData), ""); + if (!_type.isDynamicallySized()) + solAssert(_type.length() < u256("0xffffffffffffffff"), ""); + solAssert(!_type.baseType()->isDynamicallyEncoded(), ""); + solAssert(_type.baseType()->calldataEncodedSize() < u256("0xffffffffffffffff"), ""); + + string functionName = + "abi_decode_" + + _type.identifier(); + return createFunction(functionName, [&]() { + string templ; + if (_type.isDynamicallySized()) + templ = R"( + // + function (offset, end) -> arrayPos, length { + length := calldataload(offset) + switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } + arrayPos := add(offset, 0x20) + switch gt(add(arrayPos, mul(, )), end) case 1 { revert(0, 0) } + } + )"; + else + templ = R"( + // + function (offset, end) -> arrayPos { + arrayPos := offset + switch gt(add(arrayPos, mul(, )), end) case 1 { revert(0, 0) } + } + )"; + Whiskers w{templ}; + w("functionName", functionName); + w("readableTypeName", _type.toString(true)); + w("baseEncodedSize", toCompactHexWithPrefix(_type.isByteArray() ? 1 : _type.baseType()->calldataEncodedSize())); + w("length", _type.isDynamicallyEncoded() ? "length" : toCompactHexWithPrefix(_type.length())); + return w.render(); + }); +} + +string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _fromMemory) +{ + solAssert(_type.dataStoredIn(DataLocation::Memory), ""); + solAssert(_type.isByteArray(), ""); + + string functionName = + "abi_decode_" + + _type.identifier() + + (_fromMemory ? "_fromMemory" : ""); + + return createFunction(functionName, [&]() { + Whiskers templ( + R"( + function (offset, end) -> array { + let length := (offset) + array := ((length)) + mstore(array, length) + let src := add(offset, 0x20) + let dst := add(array, 0x20) + switch gt(add(src, length), end) case 1 { revert(0, 0) } + (src, dst, length) + } + )" + ); + templ("functionName", functionName); + templ("load", _fromMemory ? "mload" : "calldataload"); + templ("allocate", allocationFunction()); + templ("allocationSize", arrayAllocationSizeFunction(_type)); + templ("copyToMemFun", copyToMemoryFunction(!_fromMemory)); + return templ.render(); + }); +} + +string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fromMemory) +{ + string functionName = + "abi_decode_" + + _type.identifier() + + (_fromMemory ? "_fromMemory" : ""); + + solUnimplementedAssert(!_type.dataStoredIn(DataLocation::CallData), ""); + + return createFunction(functionName, [&]() { + Whiskers templ(R"( + // + function (headStart, end) -> value { + switch slt(sub(end, headStart), ) case 1 { revert(0, 0) } + value := () + <#members> + { + // + + } + + } + )"); + templ("functionName", functionName); + templ("readableTypeName", _type.toString(true)); + templ("allocate", allocationFunction()); + solAssert(_type.memorySize() < u256("0xffffffffffffffff"), ""); + templ("memorySize", toCompactHexWithPrefix(_type.memorySize())); + size_t headPos = 0; + vector> members; + for (auto const& member: _type.members(nullptr)) + { + solAssert(member.type, ""); + solAssert(member.type->canLiveOutsideStorage(), ""); + auto decodingType = member.type->decodingType(); + solAssert(decodingType, ""); + bool dynamic = decodingType->isDynamicallyEncoded(); + Whiskers memberTempl(R"( + let offset := )" + string(dynamic ? "(add(headStart, ))" : "" ) + R"( + mstore(add(value, ), (add(headStart, offset), end)) + )"); + memberTempl("load", _fromMemory ? "mload" : "calldataload"); + memberTempl("pos", to_string(headPos)); + memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name))); + memberTempl("abiDecode", abiDecodingFunction(*member.type, _fromMemory, false)); + + members.push_back({}); + members.back()["decode"] = memberTempl.render(); + members.back()["memberName"] = member.name; + headPos += dynamic ? 0x20 : decodingType->calldataEncodedSize(); + } + templ("members", members); + templ("minimumSize", toCompactHexWithPrefix(headPos)); + return templ.render(); + }); +} + +string ABIFunctions::abiDecodingFunctionFunctionType(FunctionType const& _type, bool _fromMemory, bool _forUseOnStack) +{ + solAssert(_type.kind() == FunctionType::Kind::External, ""); + + string functionName = + "abi_decode_" + + _type.identifier() + + (_fromMemory ? "_fromMemory" : "") + + (_forUseOnStack ? "_onStack" : ""); + + return createFunction(functionName, [&]() { + if (_forUseOnStack) + { + return Whiskers(R"( + function (offset, end) -> addr, function_selector { + addr, function_selector := ((offset)) + } + )") + ("functionName", functionName) + ("load", _fromMemory ? "mload" : "calldataload") + ("splitExtFun", splitExternalFunctionIdFunction()) + .render(); + } + else + { + return Whiskers(R"( + function (offset, end) -> fun { + fun := ((offset)) + } + )") + ("functionName", functionName) + ("load", _fromMemory ? "mload" : "calldataload") + ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction()) + .render(); + } + }); +} + string ABIFunctions::copyToMemoryFunction(bool _fromCalldata) { string functionName = "copy_" + string(_fromCalldata ? "calldata" : "memory") + "_to_memory"; @@ -1098,6 +1470,33 @@ string ABIFunctions::arrayLengthFunction(ArrayType const& _type) }); } +string ABIFunctions::arrayAllocationSizeFunction(ArrayType const& _type) +{ + solAssert(_type.dataStoredIn(DataLocation::Memory), ""); + string functionName = "array_allocation_size_" + _type.identifier(); + return createFunction(functionName, [&]() { + Whiskers w(R"( + function (length) -> size { + // Make sure we can allocate memory without overflow + switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } + size := + + } + )"); + w("functionName", functionName); + if (_type.isByteArray()) + // Round up + w("allocationSize", "and(add(length, 0x1f), not(0x1f))"); + else + w("allocationSize", "mul(length, 0x20)"); + if (_type.isDynamicallySized()) + w("addLengthSlot", "size := add(size, 0x20)"); + else + w("addLengthSlot", ""); + return w.render(); + }); +} + string ABIFunctions::arrayDataAreaFunction(ArrayType const& _type) { string functionName = "array_dataslot_" + _type.identifier(); @@ -1189,6 +1588,24 @@ string ABIFunctions::nextArrayElementFunction(ArrayType const& _type) }); } +string ABIFunctions::allocationFunction() +{ + string functionName = "allocateMemory"; + return createFunction(functionName, [&]() { + return Whiskers(R"( + function (size) -> memPtr { + memPtr := mload() + let newFreePtr := add(memPtr, size) + switch lt(newFreePtr, memPtr) case 1 { revert(0, 0) } + mstore(, newFreePtr) + } + )") + ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) + ("functionName", functionName) + .render(); + }); +} + string ABIFunctions::createFunction(string const& _name, function const& _creator) { if (!m_requestedFunctions.count(_name)) diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index e61f68bc..855b2a11 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -66,6 +66,16 @@ public: bool _encodeAsLibraryTypes = false ); + /// @returns name of an assembly function to ABI-decode values of @a _types + /// into memory. If @a _fromMemory is true, decodes from memory instead of + /// from calldata. + /// Can allocate memory. + /// Inputs: (layout reversed on stack) + /// Outputs: ... + /// The values represent stack slots. If a type occupies more or less than one + /// stack slot, it takes exactly that number of values. + std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false); + /// @returns concatenation of all generated functions. std::string requestedFunctions(); @@ -87,6 +97,10 @@ private: /// for use in the ABI. std::string combineExternalFunctionIdFunction(); + /// @returns a function that splits the address and selector from a single value + /// for use in the ABI. + std::string splitExternalFunctionIdFunction(); + /// @returns the name of the ABI encoding function with the given type /// and queues the generation of the function to the requested functions. /// @param _fromStack if false, the input value was just loaded from storage @@ -146,6 +160,29 @@ private: bool _fromStack ); + /// @returns the name of the ABI decodinf function for the given type + /// and queues the generation of the function to the requested functions. + /// The caller has to ensure that no out of bounds access (at least to the static + /// part) can happen inside this function. + /// @param _fromMemory if decoding from memory instead of from calldata + /// @param _forUseOnStack if the decoded value is stored on stack or in memory. + std::string abiDecodingFunction( + Type const& _Type, + bool _fromMemory, + bool _forUseOnStack + ); + + /// Part of @a abiDecodingFunction for "regular" array types. + 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 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); + /// @returns a function that copies raw bytes of dynamic length from calldata /// or memory to memory. /// Pads with zeros and might write more than exactly length. @@ -158,6 +195,10 @@ private: std::string roundUpFunction(); std::string arrayLengthFunction(ArrayType const& _type); + /// @returns the name of a function that computes the number of bytes required + /// to store an array in memory given its length (internally encoded, not ABI encoded). + /// The function reverts for too large lengthes. + std::string arrayAllocationSizeFunction(ArrayType const& _type); /// @returns the name of a function that converts a storage slot number /// or a memory pointer to the slot number / memory pointer for the data position of an array /// which is stored in that slot / memory area. @@ -166,6 +207,12 @@ private: /// Only works for memory arrays and storage arrays that store one item per slot. std::string nextArrayElementFunction(ArrayType const& _type); + /// @returns the name of a function that allocates memory. + /// Modifies the "free memory pointer" + /// Arguments: size + /// Return value: pointer + std::string allocationFunction(); + /// Helper function that uses @a _creator to create a function and add it to /// @a m_requestedFunctions if it has not been created yet and returns @a _name in both /// cases. diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ce9c3b7f..35d763f1 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -318,6 +318,7 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); +// cout << _assembly << endl; auto scanner = make_shared(CharStream(_assembly), "--CODEGEN--"); auto parserResult = assembly::Parser(errorReporter).parse(scanner); #ifdef SOL_OUTPUT_ASM diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 053bed6a..533aca5c 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -319,6 +319,23 @@ void CompilerUtils::abiEncodeV2( m_context << ret.tag(); } +void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory) +{ + // stack: + auto ret = m_context.pushNewTag(); + m_context << Instruction::SWAP1; + if (_fromMemory) + // TODO pass correct size for the memory case + m_context << (u256(1) << 63); + else + m_context << Instruction::CALLDATASIZE; + m_context << Instruction::SWAP1; + string decoderName = m_context.abiFunctions().tupleDecoder(_parameterTypes, _fromMemory); + m_context.appendJumpTo(m_context.namedTag(decoderName)); + m_context.adjustStackOffset(int(sizeOnStack(_parameterTypes)) - 3); + m_context << ret.tag(); +} + void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) { auto repeat = m_context.newTag(); diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index ad3989ad..3cde281b 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -146,6 +146,13 @@ public: bool _encodeAsLibraryTypes = false ); + /// Decodes data from ABI encoding into internal encoding. If @a _fromMemory is set to true, + /// the data is taken from memory instead of from calldata. + /// Can allocate memory. + /// Stack pre: + /// Stack post: ... + void abiDecodeV2(TypePointers const& _parameterTypes, bool _fromMemory = false); + /// Zero-initialises (the data part of) an already allocated memory array. /// Length has to be nonzero! /// Stack pre: diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 74565ae4..1ae44165 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -322,6 +322,15 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter { // We do not check the calldata size, everything is zero-padded + if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) + { + // Use the new JULIA-based decoding function + auto stackHeightBefore = m_context.stackHeight(); + CompilerUtils(m_context).abiDecodeV2(_typeParameters, _fromMemory); + solAssert(m_context.stackHeight() - stackHeightBefore == CompilerUtils(m_context).sizeOnStack(_typeParameters) - 1, ""); + return; + } + //@todo this does not yet support nested dynamic arrays // Retain the offset pointer as base_offset, the point from which the data offsets are computed. @@ -892,6 +901,7 @@ void ContractCompiler::appendMissingFunctions() } m_context.appendMissingLowLevelFunctions(); string abiFunctions = m_context.abiFunctions().requestedFunctions(); +// cout << abiFunctions << endl; if (!abiFunctions.empty()) m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true); } -- cgit v1.2.3 From 5a3dbb0269b3ff6b443a3cb4ccfc4f00eaba26b4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 13 Oct 2017 17:29:09 +0200 Subject: Cleanup and overflow checks for data pointers. --- libsolidity/codegen/ABIFunctions.cpp | 64 ++++++++++++++++++++++---------- libsolidity/codegen/ABIFunctions.h | 4 +- libsolidity/codegen/CompilerContext.cpp | 1 - libsolidity/codegen/ContractCompiler.cpp | 1 - 4 files changed, 47 insertions(+), 23 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index c9a9ff57..6648be06 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -111,9 +111,9 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) if (_fromMemory) functionName += "_fromMemory"; - return createFunction(functionName, [&]() { - solAssert(!_types.empty(), ""); + solAssert(!_types.empty(), ""); + return createFunction(functionName, [&]() { TypePointers decodingTypes; for (auto const& t: _types) decodingTypes.emplace_back(t->decodingType()); @@ -146,16 +146,22 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) stackPos++; } bool dynamic = decodingTypes[i]->isDynamicallyEncoded(); - Whiskers elementTempl(R"( + Whiskers elementTempl( + dynamic ? + R"( { - let offset := )" + string( - dynamic ? - "(add(headStart, ))" : - "" - ) + R"( + let offset := (add(headStart, )) + switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) } := (add(headStart, offset), dataEnd) } - )"); + )" : + R"( + { + let offset := + := (add(headStart, offset), dataEnd) + } + )" + ); elementTempl("load", _fromMemory ? "mload" : "calldataload"); elementTempl("values", boost::algorithm::join(valueNamesLocal, ", ")); elementTempl("pos", to_string(headPos)); @@ -1053,6 +1059,10 @@ string ABIFunctions::abiEncodingFunctionFunctionType( string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bool _forUseOnStack) { + // The decoding function has to perform bounds checks unless it decodes a value type. + // Conversely, bounds checks have to be performed before the decoding function + // of a value type is called. + TypePointer decodingType = _type.decodingType(); solAssert(decodingType, ""); @@ -1072,7 +1082,14 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo return abiDecodingFunctionStruct(*structType, _fromMemory); else if (auto const* functionType = dynamic_cast(decodingType.get())) return abiDecodingFunctionFunctionType(*functionType, _fromMemory, _forUseOnStack); + else + return abiDecodingFunctionValueType(_type, _fromMemory); +} +string ABIFunctions::abiDecodingFunctionValueType(const Type& _type, bool _fromMemory) +{ + TypePointer decodingType = _type.decodingType(); + solAssert(decodingType, ""); solAssert(decodingType->sizeOnStack() == 1, ""); solAssert(decodingType->isValueType(), ""); solAssert(decodingType->calldataEncodedSize() == 32, ""); @@ -1095,6 +1112,7 @@ string ABIFunctions::abiDecodingFunction(Type const& _type, bool _fromMemory, bo templ("cleanup", cleanupFunction(_type, true)); return templ.render(); }); + } string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory) @@ -1116,6 +1134,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from R"( // function (offset, end) -> array { + switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } let length := array := ((length)) let dst := array @@ -1125,7 +1144,6 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from for { let i := 0 } lt(i, length) { i := add(i, 1) } { let elementPos := - mstore(dst, (elementPos, end)) dst := add(dst, 0x20) src := add(src, ) @@ -1145,10 +1163,6 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from if (dynamicBase) { templ("staticBoundsCheck", ""); - // The dynamic bounds check might not be needed (because we have an additional check - // one level deeper), but we keep it in just in case. This at least prevents - // the part one level deeper from reading the length from an out of bounds position. - templ("dynamicBoundsCheck", "switch gt(elementPos, end) case 1 { revert(0, 0) }"); templ("retrieveElementPos", "add(offset, " + load + "(src))"); templ("baseEncodedSize", "0x20"); } @@ -1156,7 +1170,6 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from { string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize()); templ("staticBoundsCheck", "switch gt(add(src, mul(length, " + baseEncodedSize + ")), end) case 1 { revert(0, 0) }"); - templ("dynamicBoundsCheck", ""); templ("retrieveElementPos", "src"); templ("baseEncodedSize", baseEncodedSize); } @@ -1184,6 +1197,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) templ = R"( // function (offset, end) -> arrayPos, length { + switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } length := calldataload(offset) switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } arrayPos := add(offset, 0x20) @@ -1221,6 +1235,7 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _ Whiskers templ( R"( function (offset, end) -> array { + switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } let length := (offset) array := ((length)) mstore(array, length) @@ -1277,10 +1292,18 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr auto decodingType = member.type->decodingType(); solAssert(decodingType, ""); bool dynamic = decodingType->isDynamicallyEncoded(); - Whiskers memberTempl(R"( - let offset := )" + string(dynamic ? "(add(headStart, ))" : "" ) + R"( - mstore(add(value, ), (add(headStart, offset), end)) - )"); + Whiskers memberTempl( + dynamic ? + R"( + let offset := (add(headStart, )) + switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) } + mstore(add(value, ), (add(headStart, offset), end)) + )" : + R"( + let offset := + mstore(add(value, ), (add(headStart, offset), end)) + )" + ); memberTempl("load", _fromMemory ? "mload" : "calldataload"); memberTempl("pos", to_string(headPos)); memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name))); @@ -1596,7 +1619,8 @@ string ABIFunctions::allocationFunction() function (size) -> memPtr { memPtr := mload() let newFreePtr := add(memPtr, size) - switch lt(newFreePtr, memPtr) case 1 { revert(0, 0) } + // protect against overflow + switch or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) case 1 { revert(0, 0) } mstore(, newFreePtr) } )") diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index 855b2a11..2b582e84 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -160,7 +160,7 @@ private: bool _fromStack ); - /// @returns the name of the ABI decodinf function for the given type + /// @returns the name of the ABI decoding function for the given type /// and queues the generation of the function to the requested functions. /// The caller has to ensure that no out of bounds access (at least to the static /// part) can happen inside this function. @@ -172,6 +172,8 @@ private: bool _forUseOnStack ); + /// Part of @a abiDecodingFunction for value types. + std::string abiDecodingFunctionValueType(Type const& _type, bool _fromMemory); /// Part of @a abiDecodingFunction for "regular" array types. std::string abiDecodingFunctionArray(ArrayType const& _type, bool _fromMemory); /// Part of @a abiDecodingFunction for calldata array types. diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 35d763f1..ce9c3b7f 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -318,7 +318,6 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); -// cout << _assembly << endl; auto scanner = make_shared(CharStream(_assembly), "--CODEGEN--"); auto parserResult = assembly::Parser(errorReporter).parse(scanner); #ifdef SOL_OUTPUT_ASM diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 1ae44165..a81ba518 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -901,7 +901,6 @@ void ContractCompiler::appendMissingFunctions() } m_context.appendMissingLowLevelFunctions(); string abiFunctions = m_context.abiFunctions().requestedFunctions(); -// cout << abiFunctions << endl; if (!abiFunctions.empty()) m_context.appendInlineAssembly("{" + move(abiFunctions) + "}", {}, true); } -- cgit v1.2.3 From d37e6ba1c7ade678644b5669b120bd76b72f39ea Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 30 Mar 2017 11:16:28 +0100 Subject: Add target selection helpers to StandardCompiler --- libsolidity/interface/StandardCompiler.cpp | 56 ++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 430739ac..9aec7abb 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -131,6 +131,62 @@ StringMap createSourceList(Json::Value const& _input) return sources; } +bool isTargetRequired(Json::Value const& _targets, string const& _target) +{ + for (auto const& target: _targets) + /// @TODO support sub-matching, e.g "evm" matches "evm.assembly" + if (target == "*" || target == _target) + return true; + return false; +} + +/// +/// @a _targets is a JSON object containining a two-level hashmap, where the first level is the filename, +/// the second level is the contract name and the value is an array of target names to be requested for that contract. +/// @a _file is the current file +/// @a _contract is the current contract +/// @a _target is the current target name +/// +/// @returns true if the @a _targets has a match for the requested target in the specific file / contract. +/// +/// In @a _targets the use of '*' as a wildcard is permitted. +/// +/// @TODO optimise this. Perhaps flatten the structure upfront. +/// +bool isTargetRequired(Json::Value const& _targets, string const& _file, string const& _contract, string const& _target) +{ + if (!_targets.isObject()) + return false; + + for (auto const& file: { _file, string("*") }) + if (_targets.isMember(file) && _targets[file].isObject()) + { + if (_contract.empty()) + { + /// Special case for SourceUnit-level targets (such as AST) + if ( + _targets[file].isMember("") && + _targets[file][""].isArray() && + isTargetRequired(_targets[file][""], _target) + ) + return true; + } + else + { + /// Regular case for Contract-level targets + for (auto const& contract: { _contract, string("*") }) + if ( + _targets[file].isMember(contract) && + _targets[file][contract].isArray() && + isTargetRequired(_targets[file][contract], _target) + ) + return true; + } + } + + return false; +} + Json::Value formatLinkReferences(std::map const& linkReferences) { Json::Value ret(Json::objectValue); -- cgit v1.2.3 From 8da245cca3228be3e8d7908f184a327dcaf16b29 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 12 Apr 2017 10:37:04 +0100 Subject: Limit output according to the selected targets in StandardCompiler --- libsolidity/interface/StandardCompiler.cpp | 52 ++++++++++++++++++------------ 1 file changed, 32 insertions(+), 20 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 9aec7abb..8a4e81e4 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -452,8 +452,10 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { Json::Value sourceResult = Json::objectValue; sourceResult["id"] = sourceIndex++; - sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName)); - sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName)); + if (isTargetRequired(outputSelection, sourceName, "", "ast")) + sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName)); + if (isTargetRequired(outputSelection, sourceName, "", "legacyAST")) + sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName)); output["sources"][sourceName] = sourceResult; } @@ -467,28 +469,38 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // ABI, documentation and metadata Json::Value contractData(Json::objectValue); - contractData["abi"] = m_compilerStack.contractABI(contractName); - contractData["metadata"] = m_compilerStack.metadata(contractName); - contractData["userdoc"] = m_compilerStack.natspecUser(contractName); - contractData["devdoc"] = m_compilerStack.natspecDev(contractName); + if (isTargetRequired(outputSelection, file, name, "abi")) + contractData["abi"] = m_compilerStack.contractABI(contractName); + if (isTargetRequired(outputSelection, file, name, "metadata")) + contractData["metadata"] = m_compilerStack.metadata(contractName); + if (isTargetRequired(outputSelection, file, name, "userdoc")) + contractData["userdoc"] = m_compilerStack.natspecUser(contractName); + if (isTargetRequired(outputSelection, file, name, "devdoc")) + contractData["devdoc"] = m_compilerStack.natspecDev(contractName); // EVM Json::Value evmData(Json::objectValue); // @TODO: add ir - evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input)); - evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input)); - evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); - evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); - - evmData["bytecode"] = collectEVMObject( - m_compilerStack.object(contractName), - m_compilerStack.sourceMapping(contractName) - ); - - evmData["deployedBytecode"] = collectEVMObject( - m_compilerStack.runtimeObject(contractName), - m_compilerStack.runtimeSourceMapping(contractName) - ); + if (isTargetRequired(outputSelection, file, name, "evm.assembly")) + evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input)); + if (isTargetRequired(outputSelection, file, name, "evm.legacyAssembly")) + evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input)); + if (isTargetRequired(outputSelection, file, name, "evm.methodIdentifiers")) + evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); + if (isTargetRequired(outputSelection, file, name, "evm.gasEstimates")) + evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); + + if (isTargetRequired(outputSelection, file, name, "evm.bytecode")) + evmData["bytecode"] = collectEVMObject( + m_compilerStack.object(contractName), + m_compilerStack.sourceMapping(contractName) + ); + + if (isTargetRequired(outputSelection, file, name, "evm.deployedBytecode")) + evmData["deployedBytecode"] = collectEVMObject( + m_compilerStack.runtimeObject(contractName), + m_compilerStack.runtimeSourceMapping(contractName) + ); contractData["evm"] = evmData; -- cgit v1.2.3 From bbcec95bac84314d947814ace7da85f69168df94 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 19 Oct 2017 14:09:40 +0100 Subject: Add workaround for bytecode/deployedBytecode selection --- libsolidity/interface/StandardCompiler.cpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 8a4e81e4..d9ad0759 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -187,6 +187,14 @@ bool isTargetRequired(Json::Value const& _targets, string const& _file, string c return false; } +bool isTargetRequired(Json::Value const& _targets, string const& _file, string const& _contract, vector const& _requested) +{ + for (auto const& requested: _requested) + if (isTargetRequired(_targets, _file, _contract, requested)) + return true; + return false; +} + Json::Value formatLinkReferences(std::map const& linkReferences) { Json::Value ret(Json::objectValue); @@ -490,13 +498,23 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) if (isTargetRequired(outputSelection, file, name, "evm.gasEstimates")) evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); - if (isTargetRequired(outputSelection, file, name, "evm.bytecode")) + if (isTargetRequired( + outputSelection, + file, + name, + { "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" } + )) evmData["bytecode"] = collectEVMObject( m_compilerStack.object(contractName), m_compilerStack.sourceMapping(contractName) ); - if (isTargetRequired(outputSelection, file, name, "evm.deployedBytecode")) + if (isTargetRequired( + outputSelection, + file, + name, + { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" } + )) evmData["deployedBytecode"] = collectEVMObject( m_compilerStack.runtimeObject(contractName), m_compilerStack.runtimeSourceMapping(contractName) -- cgit v1.2.3 From b2023196a295a0e41bdc5c2b848b5c09fc581691 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 12:55:44 +0000 Subject: Rename target selection to use the word artifact --- libsolidity/interface/StandardCompiler.cpp | 64 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index d9ad0759..6fb70584 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -131,43 +131,43 @@ StringMap createSourceList(Json::Value const& _input) return sources; } -bool isTargetRequired(Json::Value const& _targets, string const& _target) +bool isArtifactRequested(Json::Value const& _outputSelection, string const& _artifact) { - for (auto const& target: _targets) + for (auto const& artifact: _outputSelection) /// @TODO support sub-matching, e.g "evm" matches "evm.assembly" - if (target == "*" || target == _target) + if (artifact == "*" || artifact == _artifact) return true; return false; } /// -/// @a _targets is a JSON object containining a two-level hashmap, where the first level is the filename, -/// the second level is the contract name and the value is an array of target names to be requested for that contract. +/// @a _outputSelection is a JSON object containining a two-level hashmap, where the first level is the filename, +/// the second level is the contract name and the value is an array of artifact names to be requested for that contract. /// @a _file is the current file /// @a _contract is the current contract -/// @a _target is the current target name +/// @a _artifact is the current artifact name /// -/// @returns true if the @a _targets has a match for the requested target in the specific file / contract. +/// @returns true if the @a _outputSelection has a match for the requested target in the specific file / contract. /// -/// In @a _targets the use of '*' as a wildcard is permitted. +/// In @a _outputSelection the use of '*' as a wildcard is permitted. /// /// @TODO optimise this. Perhaps flatten the structure upfront. /// -bool isTargetRequired(Json::Value const& _targets, string const& _file, string const& _contract, string const& _target) +bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, string const& _artifact) { - if (!_targets.isObject()) + if (!_outputSelection.isObject()) return false; for (auto const& file: { _file, string("*") }) - if (_targets.isMember(file) && _targets[file].isObject()) + if (_outputSelection.isMember(file) && _outputSelection[file].isObject()) { if (_contract.empty()) { /// Special case for SourceUnit-level targets (such as AST) if ( - _targets[file].isMember("") && - _targets[file][""].isArray() && - isTargetRequired(_targets[file][""], _target) + _outputSelection[file].isMember("") && + _outputSelection[file][""].isArray() && + isArtifactRequested(_outputSelection[file][""], _artifact) ) return true; } @@ -176,9 +176,9 @@ bool isTargetRequired(Json::Value const& _targets, string const& _file, string c /// Regular case for Contract-level targets for (auto const& contract: { _contract, string("*") }) if ( - _targets[file].isMember(contract) && - _targets[file][contract].isArray() && - isTargetRequired(_targets[file][contract], _target) + _outputSelection[file].isMember(contract) && + _outputSelection[file][contract].isArray() && + isArtifactRequested(_outputSelection[file][contract], _artifact) ) return true; } @@ -187,10 +187,10 @@ bool isTargetRequired(Json::Value const& _targets, string const& _file, string c return false; } -bool isTargetRequired(Json::Value const& _targets, string const& _file, string const& _contract, vector const& _requested) +bool isArtifactRequested(Json::Value const& _outputSelection, string const& _file, string const& _contract, vector const& _artifacts) { - for (auto const& requested: _requested) - if (isTargetRequired(_targets, _file, _contract, requested)) + for (auto const& artifact: _artifacts) + if (isArtifactRequested(_outputSelection, _file, _contract, artifact)) return true; return false; } @@ -460,9 +460,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) { Json::Value sourceResult = Json::objectValue; sourceResult["id"] = sourceIndex++; - if (isTargetRequired(outputSelection, sourceName, "", "ast")) + if (isArtifactRequested(outputSelection, sourceName, "", "ast")) sourceResult["ast"] = ASTJsonConverter(false, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName)); - if (isTargetRequired(outputSelection, sourceName, "", "legacyAST")) + if (isArtifactRequested(outputSelection, sourceName, "", "legacyAST")) sourceResult["legacyAST"] = ASTJsonConverter(true, m_compilerStack.sourceIndices()).toJson(m_compilerStack.ast(sourceName)); output["sources"][sourceName] = sourceResult; } @@ -477,28 +477,28 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) // ABI, documentation and metadata Json::Value contractData(Json::objectValue); - if (isTargetRequired(outputSelection, file, name, "abi")) + if (isArtifactRequested(outputSelection, file, name, "abi")) contractData["abi"] = m_compilerStack.contractABI(contractName); - if (isTargetRequired(outputSelection, file, name, "metadata")) + if (isArtifactRequested(outputSelection, file, name, "metadata")) contractData["metadata"] = m_compilerStack.metadata(contractName); - if (isTargetRequired(outputSelection, file, name, "userdoc")) + if (isArtifactRequested(outputSelection, file, name, "userdoc")) contractData["userdoc"] = m_compilerStack.natspecUser(contractName); - if (isTargetRequired(outputSelection, file, name, "devdoc")) + if (isArtifactRequested(outputSelection, file, name, "devdoc")) contractData["devdoc"] = m_compilerStack.natspecDev(contractName); // EVM Json::Value evmData(Json::objectValue); // @TODO: add ir - if (isTargetRequired(outputSelection, file, name, "evm.assembly")) + if (isArtifactRequested(outputSelection, file, name, "evm.assembly")) evmData["assembly"] = m_compilerStack.assemblyString(contractName, createSourceList(_input)); - if (isTargetRequired(outputSelection, file, name, "evm.legacyAssembly")) + if (isArtifactRequested(outputSelection, file, name, "evm.legacyAssembly")) evmData["legacyAssembly"] = m_compilerStack.assemblyJSON(contractName, createSourceList(_input)); - if (isTargetRequired(outputSelection, file, name, "evm.methodIdentifiers")) + if (isArtifactRequested(outputSelection, file, name, "evm.methodIdentifiers")) evmData["methodIdentifiers"] = m_compilerStack.methodIdentifiers(contractName); - if (isTargetRequired(outputSelection, file, name, "evm.gasEstimates")) + if (isArtifactRequested(outputSelection, file, name, "evm.gasEstimates")) evmData["gasEstimates"] = m_compilerStack.gasEstimates(contractName); - if (isTargetRequired( + if (isArtifactRequested( outputSelection, file, name, @@ -509,7 +509,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.sourceMapping(contractName) ); - if (isTargetRequired( + if (isArtifactRequested( outputSelection, file, name, -- cgit v1.2.3 From 3576ccf5b36f41d36898919eee7316b9c7c49d41 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 13:35:01 +0000 Subject: Simplify target selection code --- libsolidity/interface/StandardCompiler.cpp | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 6fb70584..ad01821e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -161,27 +161,18 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil for (auto const& file: { _file, string("*") }) if (_outputSelection.isMember(file) && _outputSelection[file].isObject()) { - if (_contract.empty()) - { - /// Special case for SourceUnit-level targets (such as AST) + /// For SourceUnit-level targets (such as AST) only allow empty name, otherwise + /// for Contract-level targets try both contract name and wildcard + vector contracts{ _contract }; + if (!_contract.empty()) + contracts.push_back("*"); + for (auto const& contract: contracts) if ( - _outputSelection[file].isMember("") && - _outputSelection[file][""].isArray() && - isArtifactRequested(_outputSelection[file][""], _artifact) + _outputSelection[file].isMember(contract) && + _outputSelection[file][contract].isArray() && + isArtifactRequested(_outputSelection[file][contract], _artifact) ) return true; - } - else - { - /// Regular case for Contract-level targets - for (auto const& contract: { _contract, string("*") }) - if ( - _outputSelection[file].isMember(contract) && - _outputSelection[file][contract].isArray() && - isArtifactRequested(_outputSelection[file][contract], _artifact) - ) - return true; - } } return false; -- cgit v1.2.3 From 9f756e37975504936b8f536474d4ea3274d5baeb Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 29 Nov 2017 21:43:28 +0000 Subject: Include missing forward declarations in AsmDataForward --- libsolidity/inlineasm/AsmDataForward.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index d627b41a..1ab62cc0 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -43,9 +43,12 @@ struct FunctionDefinition; struct FunctionCall; struct If; struct Switch; +struct Case; struct ForLoop; struct Block; +struct TypedName; + using Statement = boost::variant; } -- cgit v1.2.3 From 19e067465a058ca9f40238cb3a928a1113531c9d Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 15:23:25 +0200 Subject: Unary operators and division. --- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/formal/SMTChecker.cpp | 191 +++++++++++++++++++++++++---------- libsolidity/formal/SMTChecker.h | 14 ++- libsolidity/formal/SolverInterface.h | 4 + libsolidity/formal/Z3Interface.cpp | 5 +- 5 files changed, 155 insertions(+), 61 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 73047e76..96160a75 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1060,7 +1060,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) _statement.initialValue()->location(), "Invalid rational " + valueComponentType->toString() + - " (absolute value too large or divison by zero)." + " (absolute value too large or division by zero)." ); else solAssert(false, ""); diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 1050621e..9e2cf908 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -55,7 +55,7 @@ void SMTChecker::analyze(SourceUnit const& _source) void SMTChecker::endVisit(VariableDeclaration const& _varDecl) { if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value()) - assignment(_varDecl, *_varDecl.value()); + assignment(_varDecl, *_varDecl.value(), _varDecl.location()); } bool SMTChecker::visit(FunctionDefinition const& _function) @@ -178,8 +178,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl) else if (knownVariable(*_varDecl.declarations()[0])) { if (_varDecl.initialValue()) - // TODO more checks? - assignment(*_varDecl.declarations()[0], *_varDecl.initialValue()); + assignment(*_varDecl.declarations()[0], *_varDecl.initialValue(), _varDecl.location()); } else m_errorReporter.warning( @@ -208,7 +207,7 @@ void SMTChecker::endVisit(Assignment const& _assignment) { Declaration const* decl = identifier->annotation().referencedDeclaration; if (knownVariable(*decl)) - assignment(*decl, _assignment.rightHandSide()); + assignment(*decl, _assignment.rightHandSide(), _assignment.location()); else m_errorReporter.warning( _assignment.location(), @@ -230,7 +229,81 @@ void SMTChecker::endVisit(TupleExpression const& _tuple) "Assertion checker does not yet implement tules and inline arrays." ); else - m_interface->addAssertion(expr(_tuple) == expr(*_tuple.components()[0])); + defineExpr(_tuple, expr(*_tuple.components()[0])); +} + +void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location) +{ + checkCondition( + _value < minValue(_type), + _location, + "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")", + "value", + &_value + ); + checkCondition( + _value > maxValue(_type), + _location, + "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")", + "value", + &_value + ); +} + +void SMTChecker::endVisit(UnaryOperation const& _op) +{ + switch (_op.getOperator()) + { + case Token::Not: // ! + { + solAssert(_op.annotation().type->category() == Type::Category::Bool, ""); + defineExpr(_op, !expr(_op.subExpression())); + break; + } + case Token::Inc: // ++ (pre- or postfix) + case Token::Dec: // -- (pre- or postfix) + { + solAssert(_op.annotation().type->category() == Type::Category::Integer, ""); + solAssert(_op.subExpression().annotation().lValueRequested, ""); + if (Identifier const* identifier = dynamic_cast(&_op.subExpression())) + { + Declaration const* decl = identifier->annotation().referencedDeclaration; + if (knownVariable(*decl)) + { + auto innerValue = currentValue(*decl); + auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1; + assignment(*decl, newValue, _op.location()); + defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement such assignments." + ); + } + else + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement such increments / decrements." + ); + break; + } + case Token::Add: // + + defineExpr(_op, expr(_op.subExpression())); + break; + case Token::Sub: // - + { + defineExpr(_op, 0 - expr(_op.subExpression())); + if (auto intType = dynamic_cast(_op.annotation().type.get())) + checkUnderOverflow(expr(_op), *intType, _op.location()); + break; + } + default: + m_errorReporter.warning( + _op.location(), + "Assertion checker does not yet implement this operator." + ); + } } void SMTChecker::endVisit(BinaryOperation const& _op) @@ -274,10 +347,8 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); + checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); m_interface->addAssertion(expr(*args[0])); - checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code"); - // TODO is there something meaningful we can check here? - // We can check whether the condition is always fulfilled or never fulfilled. } } @@ -290,7 +361,7 @@ void SMTChecker::endVisit(Identifier const& _identifier) // Will be translated as part of the node that requested the lvalue. } else if (dynamic_cast(_identifier.annotation().type.get())) - m_interface->addAssertion(expr(_identifier) == currentValue(*decl)); + defineExpr(_identifier, currentValue(*decl)); else if (FunctionType const* fun = dynamic_cast(_identifier.annotation().type.get())) { if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require) @@ -306,10 +377,10 @@ void SMTChecker::endVisit(Literal const& _literal) if (RationalNumberType const* rational = dynamic_cast(&type)) solAssert(!rational->isFractional(), ""); - m_interface->addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal))); + defineExpr(_literal, smt::Expression(type.literalValue(&_literal))); } else if (type.category() == Type::Category::Bool) - m_interface->addAssertion(expr(_literal) == smt::Expression(_literal.token() == Token::TrueLiteral ? true : false)); + defineExpr(_literal, smt::Expression(_literal.token() == Token::TrueLiteral ? true : false)); else m_errorReporter.warning( _literal.location(), @@ -326,6 +397,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) case Token::Add: case Token::Sub: case Token::Mul: + case Token::Div: { solAssert(_op.annotation().commonType, ""); solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); @@ -335,27 +407,19 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : + op == Token::Div ? left / right : /*op == Token::Mul*/ left * right ); - // Overflow check - auto const& intType = dynamic_cast(*_op.annotation().commonType); - checkCondition( - value < minValue(intType), - _op.location(), - "Underflow (resulting value less than " + formatNumber(intType.minValue()) + ")", - "value", - &value - ); - checkCondition( - value > maxValue(intType), - _op.location(), - "Overflow (resulting value larger than " + formatNumber(intType.maxValue()) + ")", - "value", - &value - ); + if (_op.getOperator() == Token::Div) + { + checkCondition(right == 0, _op.location(), "Division by zero", "value", &right); + m_interface->addAssertion(right != 0); + } + + checkUnderOverflow(value, dynamic_cast(*_op.annotation().commonType), _op.location()); - m_interface->addAssertion(expr(_op) == value); + defineExpr(_op, value); break; } default: @@ -383,7 +447,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op) /*op == Token::GreaterThanOrEqual*/ (left >= right) ); // TODO: check that other values for op are not possible. - m_interface->addAssertion(expr(_op) == value); + defineExpr(_op, value); } else m_errorReporter.warning( @@ -400,9 +464,9 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) { // @TODO check that both of them are not constant if (_op.getOperator() == Token::And) - m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression())); + defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression())); else - m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression())); + defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression())); } else m_errorReporter.warning( @@ -411,11 +475,17 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) ); } -void SMTChecker::assignment(Declaration const& _variable, Expression const& _value) +void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location) { - // TODO more checks? - // TODO add restrictions about type (might be assignment from smaller type) - m_interface->addAssertion(newValue(_variable) == expr(_value)); + assignment(_variable, expr(_value), _location); +} + +void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location) +{ + TypePointer type = _variable.type(); + if (auto const* intType = dynamic_cast(type.get())) + checkUnderOverflow(_value, *intType, _location); + m_interface->addAssertion(newValue(_variable) == _value); } void SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) @@ -694,29 +764,38 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - if (!m_expressions.count(&_e)) + solAssert(m_expressions.count(&_e), ""); + return m_expressions.at(&_e); +} + +void SMTChecker::createExpr(Expression const& _e) +{ + solAssert(!m_expressions.count(&_e), ""); + solAssert(_e.annotation().type, ""); + switch (_e.annotation().type->category()) { - solAssert(_e.annotation().type, ""); - switch (_e.annotation().type->category()) - { - case Type::Category::RationalNumber: - { - if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) - solAssert(!rational->isFractional(), ""); - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - } - case Type::Category::Integer: - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - case Type::Category::Bool: - m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); - break; - default: - solAssert(false, "Type not implemented."); - } + case Type::Category::RationalNumber: + { + if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) + solAssert(!rational->isFractional(), ""); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; } - return m_expressions.at(&_e); + case Type::Category::Integer: + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + case Type::Category::Bool: + m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + break; + default: + solAssert(false, "Type not implemented."); + } +} + +void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value) +{ + createExpr(_e); + m_interface->addAssertion(expr(_e) == _value); } smt::Expression SMTChecker::var(Declaration const& _decl) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 8e07d74d..cc3aca81 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -57,6 +57,7 @@ private: virtual void endVisit(ExpressionStatement const& _node) override; virtual void endVisit(Assignment const& _node) override; virtual void endVisit(TupleExpression const& _node) override; + virtual void endVisit(UnaryOperation const& _node) override; virtual void endVisit(BinaryOperation const& _node) override; virtual void endVisit(FunctionCall const& _node) override; virtual void endVisit(Identifier const& _node) override; @@ -66,7 +67,8 @@ private: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); - void assignment(Declaration const& _variable, Expression const& _value); + void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); + void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); // Visits the branch given by the statement, pushes and pops the SMT checker. // @param _condition if present, asserts that this condition is true within the branch. @@ -88,6 +90,9 @@ private: Expression const& _condition, std::string const& _description ); + /// Checks that the value is in the range given by the type. + void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, SourceLocation const& _location); + std::pair> checkSatisifableAndGenerateModel(std::vector const& _expressionsToEvaluate); @@ -126,9 +131,12 @@ private: using VariableSequenceCounters = std::map; - /// Returns the expression corresponding to the AST node. Creates a new expression - /// if it does not exist yet. + /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); + /// Creates the expression (value can be arbitrary) + void createExpr(Expression const& _e); + /// Creates the expression and sets its value. + void defineExpr(Expression const& _e, smt::Expression _value); /// Returns the function declaration corresponding to the given variable. /// The function takes one argument which is the "sequence number". smt::Expression var(Declaration const& _decl); diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index c9adf863..74c993e8 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -120,6 +120,10 @@ public: { return Expression("*", std::move(_a), std::move(_b), Sort::Int); } + friend Expression operator/(Expression _a, Expression _b) + { + return Expression("/", std::move(_a), std::move(_b), Sort::Int); + } Expression operator()(Expression _a) const { solAssert( diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index e5c1aef4..769e6edb 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -127,7 +127,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) {">=", 2}, {"+", 2}, {"-", 2}, - {"*", 2} + {"*", 2}, + {"/", 2} }; string const& n = _expr.name; if (m_functions.count(n)) @@ -173,6 +174,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) return arguments[0] - arguments[1]; else if (n == "*") return arguments[0] * arguments[1]; + else if (n == "/") + return arguments[0] / arguments[1]; // Cannot reach here. solAssert(false, ""); return arguments[0]; -- cgit v1.2.3 From d160ec8595171e16dd404e9b859aa37aed2fe0a4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 19:20:46 +0200 Subject: Fix signed division. --- libsolidity/formal/SMTChecker.cpp | 18 ++++++++++++++++-- libsolidity/formal/SMTChecker.h | 4 ++++ 2 files changed, 20 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 9e2cf908..c20c63c6 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -401,13 +401,14 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) { solAssert(_op.annotation().commonType, ""); solAssert(_op.annotation().commonType->category() == Type::Category::Integer, ""); + auto const& intType = dynamic_cast(*_op.annotation().commonType); smt::Expression left(expr(_op.leftExpression())); smt::Expression right(expr(_op.rightExpression())); Token::Value op = _op.getOperator(); smt::Expression value( op == Token::Add ? left + right : op == Token::Sub ? left - right : - op == Token::Div ? left / right : + op == Token::Div ? division(left, right, intType) : /*op == Token::Mul*/ left * right ); @@ -417,7 +418,7 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op) m_interface->addAssertion(right != 0); } - checkUnderOverflow(value, dynamic_cast(*_op.annotation().commonType), _op.location()); + checkUnderOverflow(value, intType, _op.location()); defineExpr(_op, value); break; @@ -475,6 +476,19 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op) ); } +smt::Expression SMTChecker::division(smt::Expression _left, smt::Expression _right, IntegerType const& _type) +{ + // Signed division in SMTLIB2 rounds differently for negative division. + if (_type.isSigned()) + return (smt::Expression::ite( + _left >= 0, + smt::Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))), + smt::Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right)) + )); + else + return _left / _right; +} + void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location) { assignment(_variable, expr(_value), _location); diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index cc3aca81..e7481cca 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -67,6 +67,10 @@ private: void compareOperation(BinaryOperation const& _op); void booleanOperation(BinaryOperation const& _op); + /// Division expression in the given type. Requires special treatment because + /// of rounding for signed division. + smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type); + void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); -- cgit v1.2.3 From a2569833203a952f265acc3511ace52cd9cec4c0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 5 Oct 2017 19:39:29 +0200 Subject: Fix expression creation problems. --- libsolidity/formal/SMTChecker.cpp | 49 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 19 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index c20c63c6..a22e35d6 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -207,7 +207,10 @@ void SMTChecker::endVisit(Assignment const& _assignment) { Declaration const* decl = identifier->annotation().referencedDeclaration; if (knownVariable(*decl)) + { assignment(*decl, _assignment.rightHandSide(), _assignment.location()); + defineExpr(_assignment, expr(_assignment.rightHandSide())); + } else m_errorReporter.warning( _assignment.location(), @@ -778,31 +781,39 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - solAssert(m_expressions.count(&_e), ""); + if (!m_expressions.count(&_e)) + { + m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." ); + createExpr(_e); + } return m_expressions.at(&_e); } void SMTChecker::createExpr(Expression const& _e) { - solAssert(!m_expressions.count(&_e), ""); - solAssert(_e.annotation().type, ""); - switch (_e.annotation().type->category()) - { - case Type::Category::RationalNumber: + if (m_expressions.count(&_e)) + m_errorReporter.warning(_e.location(), "Internal error: Expression created twice in SMT solver." ); + else { - if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) - solAssert(!rational->isFractional(), ""); - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - } - case Type::Category::Integer: - m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); - break; - case Type::Category::Bool: - m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); - break; - default: - solAssert(false, "Type not implemented."); + solAssert(_e.annotation().type, ""); + switch (_e.annotation().type->category()) + { + case Type::Category::RationalNumber: + { + if (RationalNumberType const* rational = dynamic_cast(_e.annotation().type.get())) + solAssert(!rational->isFractional(), ""); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + } + case Type::Category::Integer: + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + break; + case Type::Category::Bool: + m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + break; + default: + solAssert(false, "Type not implemented."); + } } } -- cgit v1.2.3 From 43bb915454e210ed7c201eb58b33c7ffe8dfbdb1 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 1 Dec 2017 14:10:49 +0100 Subject: Rename arguments to paramaters and returns to returnVariables. --- libsolidity/inlineasm/AsmAnalysis.cpp | 4 ++-- libsolidity/inlineasm/AsmData.h | 2 +- libsolidity/inlineasm/AsmParser.cpp | 4 ++-- libsolidity/inlineasm/AsmPrinter.cpp | 6 +++--- libsolidity/inlineasm/AsmScopeFiller.cpp | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 2804ddfc..c55355e2 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -217,14 +217,14 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef) Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); solAssert(virtualBlock, ""); Scope& varScope = scope(virtualBlock); - for (auto const& var: _funDef.arguments + _funDef.returns) + for (auto const& var: _funDef.parameters + _funDef.returnVariables) { expectValidType(var.type, var.location); m_activeVariables.insert(&boost::get(varScope.identifiers.at(var.name))); } int const stackHeight = m_stackHeight; - m_stackHeight = _funDef.arguments.size() + _funDef.returns.size(); + m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size(); bool success = (*this)(_funDef.body); diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index a792a1b8..e1753af4 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -67,7 +67,7 @@ struct VariableDeclaration { SourceLocation location; TypedNameList variables; s /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") -struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; }; +struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. struct If { SourceLocation location; std::shared_ptr condition; Block body; }; /// Switch case or default case diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 8f171005..1555804b 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -419,7 +419,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::LParen); while (currentToken() != Token::RParen) { - funDef.arguments.emplace_back(parseTypedName()); + funDef.parameters.emplace_back(parseTypedName()); if (currentToken() == Token::RParen) break; expectToken(Token::Comma); @@ -431,7 +431,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() expectToken(Token::GreaterThan); while (true) { - funDef.returns.emplace_back(parseTypedName()); + funDef.returnVariables.emplace_back(parseTypedName()); if (currentToken() == Token::LBrace) break; expectToken(Token::Comma); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 0f183244..804fb1d5 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -144,17 +144,17 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin { string out = "function " + _functionDefinition.name + "("; out += boost::algorithm::join( - _functionDefinition.arguments | boost::adaptors::transformed( + _functionDefinition.parameters | boost::adaptors::transformed( [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } ), ", " ); out += ")"; - if (!_functionDefinition.returns.empty()) + if (!_functionDefinition.returnVariables.empty()) { out += " -> "; out += boost::algorithm::join( - _functionDefinition.returns | boost::adaptors::transformed( + _functionDefinition.returnVariables | boost::adaptors::transformed( [this](TypedName argument) { return argument.name + appendTypeName(argument.type); } ), ", " diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 77ae9102..0984e7d2 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -71,10 +71,10 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) { bool success = true; vector arguments; - for (auto const& _argument: _funDef.arguments) + for (auto const& _argument: _funDef.parameters) arguments.push_back(_argument.type); vector returns; - for (auto const& _return: _funDef.returns) + for (auto const& _return: _funDef.returnVariables) returns.push_back(_return.type); if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) { @@ -91,7 +91,7 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef) varScope.superScope = m_currentScope; m_currentScope = &varScope; varScope.functionScope = true; - for (auto const& var: _funDef.arguments + _funDef.returns) + for (auto const& var: _funDef.parameters + _funDef.returnVariables) if (!registerVariable(var, _funDef.location, varScope)) success = false; -- cgit v1.2.3 From 745eefa36f9bc04c91cb28e81bd16f8d01a11c7c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 13:44:20 +0000 Subject: Split Instruction and FunctionalInstruction in Julia --- libsolidity/analysis/ViewPureChecker.cpp | 14 +++++++++----- libsolidity/inlineasm/AsmAnalysis.cpp | 7 ++++--- libsolidity/inlineasm/AsmData.h | 2 +- libsolidity/inlineasm/AsmParser.cpp | 7 ++++--- libsolidity/inlineasm/AsmPrinter.cpp | 2 +- 5 files changed, 19 insertions(+), 13 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 7e41fc16..6788cb05 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -40,16 +40,13 @@ public: void operator()(assembly::Label const&) { } void operator()(assembly::Instruction const& _instruction) { - if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction)) - m_reportMutability(StateMutability::NonPayable, _instruction.location); - else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction)) - m_reportMutability(StateMutability::View, _instruction.location); + checkInstruction(_instruction.location, _instruction.instruction); } void operator()(assembly::Literal const&) {} void operator()(assembly::Identifier const&) {} void operator()(assembly::FunctionalInstruction const& _instr) { - (*this)(_instr.instruction); + checkInstruction(_instr.location, _instr.instruction); for (auto const& arg: _instr.arguments) boost::apply_visitor(*this, arg); } @@ -102,6 +99,13 @@ public: private: std::function m_reportMutability; + void checkInstruction(SourceLocation _location, solidity::Instruction _instruction) + { + if (eth::SemanticInformation::invalidInViewFunctions(_instruction)) + m_reportMutability(StateMutability::NonPayable, _location); + else if (eth::SemanticInformation::invalidInPureFunctions(_instruction)) + m_reportMutability(StateMutability::View, _location); + } }; } diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index c55355e2..5c03342d 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -146,10 +146,11 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) if (!expectExpression(arg)) success = false; // Parser already checks that the number of arguments is correct. - solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), ""); - if (!(*this)(_instr.instruction)) - success = false; + auto const& info = instructionInfo(_instr.instruction); + solAssert(info.args == int(_instr.arguments.size()), ""); + m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instr] = m_stackHeight; + warnOnInstructions(_instr.instruction, _instr.location); return success; } diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index e1753af4..11e56fae 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -60,7 +60,7 @@ struct StackAssignment { SourceLocation location; Identifier variableName; }; /// the same amount of items as the number of variables. struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" -struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector arguments; }; +struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector arguments; }; struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr value; }; diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 1555804b..4f8802a0 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -448,10 +448,11 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) if (_instruction.type() == typeid(Instruction)) { solAssert(!m_julia, "Instructions are invalid in JULIA"); + Instruction const& instruction = std::move(boost::get(_instruction)); FunctionalInstruction ret; - ret.instruction = std::move(boost::get(_instruction)); - ret.location = ret.instruction.location; - solidity::Instruction instr = ret.instruction.instruction; + ret.instruction = instruction.instruction; + ret.location = std::move(instruction.location); + solidity::Instruction instr = ret.instruction; InstructionInfo instrInfo = instructionInfo(instr); if (solidity::isDupInstruction(instr)) fatalParserError("DUPi instructions not allowed for functional notation"); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index 804fb1d5..c72586cb 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -94,7 +94,7 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional { solAssert(!m_julia, ""); return - (*this)(_functionalInstruction.instruction) + + boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + "(" + boost::algorithm::join( _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), -- cgit v1.2.3 From bc875f6b9c140d351ec8ae96d2c8e29979017d57 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 10:44:28 +0000 Subject: Warn for assembly labels too --- libsolidity/inlineasm/AsmAnalysis.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 5c03342d..04b8417f 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -56,6 +56,7 @@ bool AsmAnalyzer::operator()(Label const& _label) { solAssert(!m_julia, ""); m_info.stackHeightInfo[&_label] = m_stackHeight; + warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; } @@ -523,10 +524,10 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio "the Metropolis hard fork. Before that it acts as an invalid instruction." ); - if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI) + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) m_errorReporter.warning( _location, - "Jump instructions are low-level EVM features that can lead to " + "Jump instructions and labels are low-level EVM features that can lead to " "incorrect stack access. Because of that they are discouraged. " "Please consider using \"switch\" or \"for\" statements instead." ); -- cgit v1.2.3 From 793537e08926190728e93a666a5fadc6d0765f63 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 5 Dec 2017 10:45:44 +0000 Subject: Suggest the "if" statement too instead of jumps --- libsolidity/inlineasm/AsmAnalysis.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 04b8417f..049af65f 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -529,6 +529,6 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio _location, "Jump instructions and labels are low-level EVM features that can lead to " "incorrect stack access. Because of that they are discouraged. " - "Please consider using \"switch\" or \"for\" statements instead." + "Please consider using \"switch\", \"if\" or \"for\" statements instead." ); } -- cgit v1.2.3 From e9d256ddf4322050f9b834abd3400f1cb5377f68 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 2 Dec 2017 01:01:55 +0000 Subject: Suggest the experimental ABI encoder if using structs as function parameters --- libsolidity/analysis/TypeChecker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 96160a75..daa5471a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -570,6 +570,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); + if ( + _function.visibility() > FunctionDefinition::Visibility::Internal && + type(*var)->category() == Type::Category::Struct && + !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) + ) + m_errorReporter.typeError( + var->location(), + "Structs are only supported in the new experimental ABI encoder. " + "Use \"pragma experimental ABIEncoderV2;\" to enable the feature." + ); var->accept(*this); } -- cgit v1.2.3 From 9e36c189e50b033d25d3ab2342a7a0e673035a68 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 Dec 2017 13:00:45 +0100 Subject: Fix struct encoding warning for libraries. --- libsolidity/analysis/TypeChecker.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index daa5471a..a578ad0f 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -573,6 +573,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) if ( _function.visibility() > FunctionDefinition::Visibility::Internal && type(*var)->category() == Type::Category::Struct && + !type(*var)->dataStoredIn(DataLocation::Storage) && !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) ) m_errorReporter.typeError( -- cgit v1.2.3 From 2d171c25e57e821c242b2a6ffa9d6e890e8b207e Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 Dec 2017 10:10:29 +0100 Subject: Limit number of secondary source locations. --- libsolidity/analysis/TypeChecker.cpp | 19 +++++-------------- libsolidity/interface/Exceptions.h | 12 ++++++++++++ 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index a578ad0f..090e5159 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -171,13 +171,7 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con ssl.append("Another declaration is here:", (*it)->location()); string msg = "More than one constructor defined."; - size_t occurrences = ssl.infos.size(); - if (occurrences > 32) - { - ssl.infos.resize(32); - msg += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; - } - + ssl.limitSize(msg); m_errorReporter.declarationError( functions[_contract.name()].front()->location(), ssl, @@ -219,12 +213,7 @@ void TypeChecker::findDuplicateDefinitions(map> const& _defini if (ssl.infos.size() > 0) { - size_t occurrences = ssl.infos.size(); - if (occurrences > 32) - { - ssl.infos.resize(32); - _message += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; - } + ssl.limitSize(_message); m_errorReporter.declarationError( overloads[i]->location(), @@ -1679,10 +1668,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) SecondarySourceLocation ssl; for (auto function: contract->annotation().unimplementedFunctions) ssl.append("Missing implementation:", function->location()); + string msg = "Trying to create an instance of an abstract contract."; + ssl.limitSize(msg); m_errorReporter.typeError( _newExpression.location(), ssl, - "Trying to create an instance of an abstract contract." + msg ); } if (!contract->constructorIsPublic()) diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h index 09301b10..7c66d572 100644 --- a/libsolidity/interface/Exceptions.h +++ b/libsolidity/interface/Exceptions.h @@ -109,6 +109,18 @@ public: infos.push_back(std::make_pair(_errMsg, _sourceLocation)); return *this; } + /// Limits the number of secondary source locations to 32 and appends a notice to the + /// error message. + void limitSize(std::string& _message) + { + size_t occurrences = infos.size(); + if (occurrences > 32) + { + infos.resize(32); + _message += " Truncated from " + boost::lexical_cast(occurrences) + " to the first 32 occurrences."; + } + } + std::vector infos; }; -- cgit v1.2.3 From 5226d54ed16bb7247c64ac67fec786301a3210ec Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 12:33:11 +0000 Subject: Improve error message for constant evaluator --- libsolidity/analysis/ReferencesResolver.cpp | 2 +- libsolidity/ast/Types.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index f22c95cc..d8030b97 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -150,7 +150,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) ConstantEvaluator e(*length, m_errorReporter); auto const* lengthType = dynamic_cast(length->annotation().type.get()); if (!lengthType || !lengthType->mobileType()) - fatalTypeError(length->location(), "Invalid array length, expected integer literal."); + fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); else if (lengthType->isFractional()) fatalTypeError(length->location(), "Array with fractional length specified."); else if (lengthType->isNegative()) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 635279ab..a54e4e09 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -257,7 +257,7 @@ public: } virtual u256 literalValue(Literal const*) const { - solAssert(false, "Literal value requested for type without literals."); + solAssert(false, "Literal value requested for type without literals: " + toString(false)); } /// @returns a (simpler) type that is encoded in the same way for external function calls. -- cgit v1.2.3 From 7ff9a85592cbb50b7a72654849582c780d7fc494 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 12:41:33 +0000 Subject: Reduce the types of errors outputted by ConstantEvaluator --- libsolidity/analysis/ConstantEvaluator.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 4d546e68..7f5ffb51 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -33,7 +33,7 @@ void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { TypePointer const& subType = _operation.subExpression().annotation().type; if (!dynamic_cast(subType.get())) - m_errorReporter.fatalTypeError(_operation.subExpression().location(), "Invalid constant expression."); + return; TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); _operation.annotation().type = t; } @@ -44,9 +44,9 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) TypePointer const& leftType = _operation.leftExpression().annotation().type; TypePointer const& rightType = _operation.rightExpression().annotation().type; if (!dynamic_cast(leftType.get())) - m_errorReporter.fatalTypeError(_operation.leftExpression().location(), "Invalid constant expression."); + return; if (!dynamic_cast(rightType.get())) - m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression."); + return; TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); if (!commonType) { @@ -71,8 +71,6 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) void ConstantEvaluator::endVisit(Literal const& _literal) { _literal.annotation().type = Type::forLiteral(_literal); - if (!_literal.annotation().type) - m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); } void ConstantEvaluator::endVisit(Identifier const& _identifier) @@ -81,11 +79,11 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) if (!variableDeclaration) return; if (!variableDeclaration->isConstant()) - m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant."); + return; - ASTPointer value = variableDeclaration->value(); + ASTPointer const& value = variableDeclaration->value(); if (!value) - m_errorReporter.fatalTypeError(_identifier.location(), "Constant identifier declaration must have a constant value."); + return; if (!value->annotation().type) { -- cgit v1.2.3 From 48c7ba72f3616a037fe3fe8cf1a490b121c416e3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 22 Nov 2017 12:54:23 +0100 Subject: Simplify ConstantEvaluator. --- libsolidity/analysis/ConstantEvaluator.cpp | 77 ++++++++++++++++------------- libsolidity/analysis/ConstantEvaluator.h | 18 +++++-- libsolidity/analysis/ReferencesResolver.cpp | 7 +-- 3 files changed, 61 insertions(+), 41 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 7f5ffb51..14171b01 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -28,49 +28,42 @@ using namespace std; using namespace dev; using namespace dev::solidity; -/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation) void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { - TypePointer const& subType = _operation.subExpression().annotation().type; - if (!dynamic_cast(subType.get())) - return; - TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); - _operation.annotation().type = t; + auto sub = type(_operation.subExpression()); + if (sub) + setType(_operation, sub->unaryOperatorResult(_operation.getOperator())); } -/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation) void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { - TypePointer const& leftType = _operation.leftExpression().annotation().type; - TypePointer const& rightType = _operation.rightExpression().annotation().type; - if (!dynamic_cast(leftType.get())) - return; - if (!dynamic_cast(rightType.get())) - return; - TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); - if (!commonType) + auto left = type(_operation.leftExpression()); + auto right = type(_operation.rightExpression()); + if (left && right) { - m_errorReporter.typeError( - _operation.location(), - "Operator " + - string(Token::toString(_operation.getOperator())) + - " not compatible with types " + - leftType->toString() + - " and " + - rightType->toString() + auto commonType = left->binaryOperatorResult(_operation.getOperator(), right); + if (!commonType) + m_errorReporter.fatalTypeError( + _operation.location(), + "Operator " + + string(Token::toString(_operation.getOperator())) + + " not compatible with types " + + left->toString() + + " and " + + right->toString() + ); + setType( + _operation, + Token::isCompareOp(_operation.getOperator()) ? + make_shared() : + left->binaryOperatorResult(_operation.getOperator(), right) ); - commonType = leftType; } - _operation.annotation().commonType = commonType; - _operation.annotation().type = - Token::isCompareOp(_operation.getOperator()) ? - make_shared() : - commonType; } void ConstantEvaluator::endVisit(Literal const& _literal) { - _literal.annotation().type = Type::forLiteral(_literal); + setType(_literal, Type::forLiteral(_literal)); } void ConstantEvaluator::endVisit(Identifier const& _identifier) @@ -84,13 +77,29 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) ASTPointer const& value = variableDeclaration->value(); if (!value) return; - - if (!value->annotation().type) + else if (!m_types->count(value.get())) { if (m_depth > 32) m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted)."); - ConstantEvaluator e(*value, m_errorReporter, m_depth + 1); + ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value); } - _identifier.annotation().type = value->annotation().type; + setType(_identifier, type(*value)); +} + +void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type) +{ + if (_type && _type->category() == Type::Category::RationalNumber) + (*m_types)[&_node] = _type; +} + +TypePointer ConstantEvaluator::type(ASTNode const& _node) +{ + return (*m_types)[&_node]; +} + +TypePointer ConstantEvaluator::evaluate(Expression const& _expr) +{ + _expr.accept(*this); + return type(_expr); } diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 6725d610..77a357b6 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -38,22 +38,32 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: - ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter, size_t _newDepth = 0): + ConstantEvaluator( + ErrorReporter& _errorReporter, + size_t _newDepth = 0, + std::shared_ptr> _types = std::make_shared>() + ): m_errorReporter(_errorReporter), - m_depth(_newDepth) + m_depth(_newDepth), + m_types(_types) { - _expr.accept(*this); } + TypePointer evaluate(Expression const& _expr); + private: virtual void endVisit(BinaryOperation const& _operation); virtual void endVisit(UnaryOperation const& _operation); virtual void endVisit(Literal const& _literal); virtual void endVisit(Identifier const& _identifier); + void setType(ASTNode const& _node, TypePointer const& _type); + TypePointer type(ASTNode const& _node); + ErrorReporter& m_errorReporter; /// Current recursion depth. - size_t m_depth; + size_t m_depth = 0; + std::shared_ptr> m_types; }; } diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index d8030b97..9eee16af 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -146,9 +146,10 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array."); if (Expression const* length = _typeName.length()) { - if (!length->annotation().type) - ConstantEvaluator e(*length, m_errorReporter); - auto const* lengthType = dynamic_cast(length->annotation().type.get()); + TypePointer lengthTypeGeneric = length->annotation().type; + if (!lengthTypeGeneric) + lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length); + RationalNumberType const* lengthType = dynamic_cast(lengthTypeGeneric.get()); if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression."); else if (lengthType->isFractional()) -- cgit v1.2.3 From e7ed9d878e80e53da9d4cc603e6527918c5a432a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 12 Dec 2017 10:45:40 +0100 Subject: Re-use `commonType` --- libsolidity/analysis/ConstantEvaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 14171b01..83f37f47 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -56,7 +56,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) _operation, Token::isCompareOp(_operation.getOperator()) ? make_shared() : - left->binaryOperatorResult(_operation.getOperator(), right) + commonType ); } } -- cgit v1.2.3 From 54b6739962ef45319777ce2aebafdf4b91412d84 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 8 Dec 2017 14:01:22 +0100 Subject: Separate expression and statement. --- libsolidity/analysis/ViewPureChecker.cpp | 4 ++ libsolidity/inlineasm/AsmAnalysis.cpp | 16 ++++-- libsolidity/inlineasm/AsmAnalysis.h | 3 +- libsolidity/inlineasm/AsmData.h | 16 +++--- libsolidity/inlineasm/AsmDataForward.h | 4 +- libsolidity/inlineasm/AsmParser.cpp | 91 ++++++++++++++++++++------------ libsolidity/inlineasm/AsmParser.h | 8 +-- libsolidity/inlineasm/AsmPrinter.cpp | 5 ++ libsolidity/inlineasm/AsmPrinter.h | 1 + libsolidity/inlineasm/AsmScopeFiller.cpp | 5 ++ libsolidity/inlineasm/AsmScopeFiller.h | 1 + 11 files changed, 104 insertions(+), 50 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 6788cb05..6257ac6d 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -50,6 +50,10 @@ public: for (auto const& arg: _instr.arguments) boost::apply_visitor(*this, arg); } + void operator()(assembly::ExpressionStatement const& _expr) + { + boost::apply_visitor(*this, _expr.expression); + } void operator()(assembly::StackAssignment const&) {} void operator()(assembly::Assignment const& _assignment) { diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 049af65f..17b7cce0 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -155,6 +155,16 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) return success; } +bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) +{ +// size_t initialStackHeight = m_stackHeight; + bool success = boost::apply_visitor(*this, _statement.expression); +// if (!expectDeposit(0, initialStackHeight, _statement.location)) +// success = false; + m_info.stackHeightInfo[&_statement] = m_stackHeight; + return success; +} + bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { solAssert(!m_julia, ""); @@ -407,13 +417,13 @@ bool AsmAnalyzer::operator()(Block const& _block) return success; } -bool AsmAnalyzer::expectExpression(Statement const& _statement) +bool AsmAnalyzer::expectExpression(Expression const& _expr) { bool success = true; int const initialHeight = m_stackHeight; - if (!boost::apply_visitor(*this, _statement)) + if (!boost::apply_visitor(*this, _expr)) success = false; - if (!expectDeposit(1, initialHeight, locationOf(_statement))) + if (!expectDeposit(1, initialHeight, locationOf(_expr))) success = false; return success; } diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index e484b876..00a33db3 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -65,6 +65,7 @@ public: bool operator()(assembly::Identifier const&); bool operator()(assembly::FunctionalInstruction const& _functionalInstruction); bool operator()(assembly::Label const& _label); + bool operator()(assembly::ExpressionStatement const&); bool operator()(assembly::StackAssignment const&); bool operator()(assembly::Assignment const& _assignment); bool operator()(assembly::VariableDeclaration const& _variableDeclaration); @@ -77,7 +78,7 @@ public: private: /// Visits the statement and expects it to deposit one item onto the stack. - bool expectExpression(Statement const& _statement); + bool expectExpression(Expression const& _expr); bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location); /// Verifies that a variable to be assigned to exists and has the same size diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h index 11e56fae..2982d5e0 100644 --- a/libsolidity/inlineasm/AsmData.h +++ b/libsolidity/inlineasm/AsmData.h @@ -58,23 +58,25 @@ struct StackAssignment { SourceLocation location; Identifier variableName; }; /// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy /// a single stack slot and expects a single expression on the right hand returning /// the same amount of items as the number of variables. -struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; +struct Assignment { SourceLocation location; std::vector variableNames; std::shared_ptr value; }; /// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" -struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector arguments; }; -struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; +struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector arguments; }; +struct FunctionCall { SourceLocation location; Identifier functionName; std::vector arguments; }; +/// Statement that contains only a single expression +struct ExpressionStatement { SourceLocation location; Expression expression; }; /// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted -struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr value; }; +struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr value; }; /// Block that creates a scope (frees declared stack variables) struct Block { SourceLocation location; std::vector statements; }; /// Function definition ("function f(a, b) -> (d, e) { ... }") struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; /// Conditional execution without "else" part. -struct If { SourceLocation location; std::shared_ptr condition; Block body; }; +struct If { SourceLocation location; std::shared_ptr condition; Block body; }; /// Switch case or default case struct Case { SourceLocation location; std::shared_ptr value; Block body; }; /// Switch statement -struct Switch { SourceLocation location; std::shared_ptr expression; std::vector cases; }; -struct ForLoop { SourceLocation location; Block pre; std::shared_ptr condition; Block post; Block body; }; +struct Switch { SourceLocation location; std::shared_ptr expression; std::vector cases; }; +struct ForLoop { SourceLocation location; Block pre; std::shared_ptr condition; Block post; Block body; }; struct LocationExtractor: boost::static_visitor { diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index 1ab62cc0..317e257c 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -45,11 +45,13 @@ struct If; struct Switch; struct Case; struct ForLoop; +struct ExpressionStatement; struct Block; struct TypedName; -using Statement = boost::variant; +using Expression = boost::variant; +using Statement = boost::variant; } } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 4f8802a0..273e1d5c 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -77,9 +77,7 @@ assembly::Statement Parser::parseStatement() { assembly::If _if = createWithLocation(); m_scanner->next(); - _if.condition = make_shared(parseExpression()); - if (_if.condition->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as conditions for if - try to append \"()\"."); + _if.condition = make_shared(parseExpression()); _if.body = parseBlock(); return _if; } @@ -87,9 +85,7 @@ assembly::Statement Parser::parseStatement() { assembly::Switch _switch = createWithLocation(); m_scanner->next(); - _switch.expression = make_shared(parseExpression()); - if (_switch.expression->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as expressions for switch - try to append \"()\"."); + _switch.expression = make_shared(parseExpression()); while (m_scanner->currentToken() == Token::Case) _switch.cases.emplace_back(parseCase()); if (m_scanner->currentToken() == Token::Default) @@ -127,18 +123,21 @@ assembly::Statement Parser::parseStatement() // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) - Statement statement(parseElementaryOperation(false)); + ElementaryOperation elementary(parseElementaryOperation(false)); switch (currentToken()) { case Token::LParen: - return parseCall(std::move(statement)); + { + Expression expr = parseCall(std::move(elementary)); + return ExpressionStatement{locationOf(expr), expr}; + } case Token::Comma: { // if a comma follows, a multiple assignment is assumed - if (statement.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); - assembly::Identifier const& identifier = boost::get(statement); + assembly::Identifier const& identifier = boost::get(elementary); Assignment assignment = createWithLocation(identifier.location); assignment.variableNames.emplace_back(identifier); @@ -146,25 +145,25 @@ assembly::Statement Parser::parseStatement() do { expectToken(Token::Comma); - statement = parseElementaryOperation(false); - if (statement.type() != typeid(assembly::Identifier)) + elementary = parseElementaryOperation(false); + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Variable name expected in multiple assignemnt."); - assignment.variableNames.emplace_back(boost::get(statement)); + assignment.variableNames.emplace_back(boost::get(elementary)); } while (currentToken() == Token::Comma); expectToken(Token::Colon); expectToken(Token::Assign); - assignment.value.reset(new Statement(parseExpression())); + assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } case Token::Colon: { - if (statement.type() != typeid(assembly::Identifier)) + if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Label name / variable name must precede \":\"."); - assembly::Identifier const& identifier = boost::get(statement); + assembly::Identifier const& identifier = boost::get(elementary); advance(); // identifier:=: should be parsed as identifier: =: (i.e. a label), // while identifier:= (being followed by a non-colon) as identifier := (assignment). @@ -175,7 +174,7 @@ assembly::Statement Parser::parseStatement() fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); - assignment.value.reset(new Statement(parseExpression())); + assignment.value.reset(new Expression(parseExpression())); assignment.location.end = locationOf(*assignment.value).end; return assignment; } @@ -194,7 +193,21 @@ assembly::Statement Parser::parseStatement() fatalParserError("Call or assignment expected."); break; } - return statement; + if (elementary.type() == typeid(assembly::Identifier)) + { + Expression expr = boost::get(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else if (elementary.type() == typeid(assembly::Literal)) + { + Expression expr = boost::get(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else + { + solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation."); + return boost::get(elementary); + } } assembly::Case Parser::parseCase() @@ -206,10 +219,10 @@ assembly::Case Parser::parseCase() else if (m_scanner->currentToken() == Token::Case) { m_scanner->next(); - assembly::Statement statement = parseElementaryOperation(); - if (statement.type() != typeid(assembly::Literal)) + ElementaryOperation literal = parseElementaryOperation(); + if (literal.type() != typeid(assembly::Literal)) fatalParserError("Literal expected."); - _case.value = make_shared(std::move(boost::get(statement))); + _case.value = make_shared(boost::get(std::move(literal))); } else fatalParserError("Case or default case expected."); @@ -224,19 +237,17 @@ assembly::ForLoop Parser::parseForLoop() ForLoop forLoop = createWithLocation(); expectToken(Token::For); forLoop.pre = parseBlock(); - forLoop.condition = make_shared(parseExpression()); - if (forLoop.condition->type() == typeid(assembly::Instruction)) - fatalParserError("Instructions are not supported as conditions for the for statement."); + forLoop.condition = make_shared(parseExpression()); forLoop.post = parseBlock(); forLoop.body = parseBlock(); forLoop.location.end = forLoop.body.location.end; return forLoop; } -assembly::Statement Parser::parseExpression() +assembly::Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); - Statement operation = parseElementaryOperation(true); + ElementaryOperation operation = parseElementaryOperation(true); if (operation.type() == typeid(Instruction)) { Instruction const& instr = boost::get(operation); @@ -252,8 +263,18 @@ assembly::Statement Parser::parseExpression() } if (currentToken() == Token::LParen) return parseCall(std::move(operation)); + else if (operation.type() == typeid(Instruction)) + { + Instruction& instr = boost::get(operation); + return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; + } + else if (operation.type() == typeid(assembly::Identifier)) + return boost::get(operation); else - return operation; + { + solAssert(operation.type() == typeid(assembly::Literal), ""); + return boost::get(operation); + } } std::map const& Parser::instructions() @@ -296,10 +317,10 @@ std::map const& Parser::instructionNames() return s_instructionNames; } -assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) +Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePusher) { RecursionGuard recursionGuard(*this); - Statement ret; + ElementaryOperation ret; switch (currentToken()) { case Token::Identifier: @@ -402,7 +423,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration() { expectToken(Token::Colon); expectToken(Token::Assign); - varDecl.value.reset(new Statement(parseExpression())); + varDecl.value.reset(new Expression(parseExpression())); varDecl.location.end = locationOf(*varDecl.value).end; } else @@ -442,13 +463,13 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition() return funDef; } -assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) +assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) { RecursionGuard recursionGuard(*this); - if (_instruction.type() == typeid(Instruction)) + if (_initialOp.type() == typeid(Instruction)) { solAssert(!m_julia, "Instructions are invalid in JULIA"); - Instruction const& instruction = std::move(boost::get(_instruction)); + Instruction& instruction = boost::get(_initialOp); FunctionalInstruction ret; ret.instruction = instruction.instruction; ret.location = std::move(instruction.location); @@ -499,10 +520,10 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction) expectToken(Token::RParen); return ret; } - else if (_instruction.type() == typeid(Identifier)) + else if (_initialOp.type() == typeid(Identifier)) { FunctionCall ret; - ret.functionName = std::move(boost::get(_instruction)); + ret.functionName = std::move(boost::get(_initialOp)); ret.location = ret.functionName.location; expectToken(Token::LParen); while (currentToken() != Token::RParen) diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index e46d1732..44889a13 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -44,6 +44,8 @@ public: std::shared_ptr parse(std::shared_ptr const& _scanner); protected: + using ElementaryOperation = boost::variant; + /// Creates an inline assembly node with the given source location. template T createWithLocation(SourceLocation const& _loc = SourceLocation()) const { @@ -65,13 +67,13 @@ protected: Case parseCase(); ForLoop parseForLoop(); /// Parses a functional expression that has to push exactly one stack element - Statement parseExpression(); + assembly::Expression parseExpression(); static std::map const& instructions(); static std::map const& instructionNames(); - Statement parseElementaryOperation(bool _onlySinglePusher = false); + ElementaryOperation parseElementaryOperation(bool _onlySinglePusher = false); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); - Statement parseCall(Statement&& _instruction); + assembly::Expression parseCall(ElementaryOperation&& _initialOp); TypedName parseTypedName(); std::string expectAsmIdentifier(); diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp index c72586cb..bacd7a94 100644 --- a/libsolidity/inlineasm/AsmPrinter.cpp +++ b/libsolidity/inlineasm/AsmPrinter.cpp @@ -102,6 +102,11 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional ")"; } +string AsmPrinter::operator()(ExpressionStatement const& _statement) +{ + return boost::apply_visitor(*this, _statement.expression); +} + string AsmPrinter::operator()(assembly::Label const& _label) { solAssert(!m_julia, ""); diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h index eadf81d9..5bd87aba 100644 --- a/libsolidity/inlineasm/AsmPrinter.h +++ b/libsolidity/inlineasm/AsmPrinter.h @@ -42,6 +42,7 @@ public: std::string operator()(assembly::Literal const& _literal); std::string operator()(assembly::Identifier const& _identifier); std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction); + std::string operator()(assembly::ExpressionStatement const& _expr); std::string operator()(assembly::Label const& _label); std::string operator()(assembly::StackAssignment const& _assignment); std::string operator()(assembly::Assignment const& _assignment); diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp index 0984e7d2..86f3809c 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.cpp +++ b/libsolidity/inlineasm/AsmScopeFiller.cpp @@ -45,6 +45,11 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): m_currentScope = &scope(nullptr); } +bool ScopeFiller::operator()(ExpressionStatement const& _expr) +{ + return boost::apply_visitor(*this, _expr.expression); +} + bool ScopeFiller::operator()(Label const& _item) { if (!m_currentScope->registerLabel(_item.name)) diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h index ed28abbf..bb023f61 100644 --- a/libsolidity/inlineasm/AsmScopeFiller.h +++ b/libsolidity/inlineasm/AsmScopeFiller.h @@ -53,6 +53,7 @@ public: bool operator()(assembly::Literal const&) { return true; } bool operator()(assembly::Identifier const&) { return true; } bool operator()(assembly::FunctionalInstruction const&) { return true; } + bool operator()(assembly::ExpressionStatement const& _expr); bool operator()(assembly::Label const& _label); bool operator()(assembly::StackAssignment const&) { return true; } bool operator()(assembly::Assignment const&) { return true; } -- cgit v1.2.3 From 2af4d7c7dd3379d8956907413258cea884c80794 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 11 Dec 2017 20:09:17 +0100 Subject: [SMTChecker] Keep track of current path conditions --- libsolidity/formal/SMTChecker.cpp | 35 +++++++++++++++++++++++++++-------- libsolidity/formal/SMTChecker.h | 9 +++++++++ libsolidity/formal/SolverInterface.h | 5 +++++ 3 files changed, 41 insertions(+), 8 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a22e35d6..61cb110e 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -71,6 +71,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function) m_interface->reset(); m_currentSequenceCounter.clear(); m_nextFreeSequenceCounter.clear(); + m_pathConditions.clear(); m_conditionalExecutionHappened = false; initializeLocalVariables(_function); return true; @@ -344,14 +345,14 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_interface->addAssertion(expr(*args[0])); + m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); } else if (funType.kind() == FunctionType::Kind::Require) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); - m_interface->addAssertion(expr(*args[0])); + m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); } } @@ -514,11 +515,11 @@ void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* { VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; - m_interface->push(); if (_condition) - m_interface->addAssertion(*_condition); + pushPathCondition(*_condition); _statement.accept(*this); - m_interface->pop(); + if (_condition) + popPathCondition(); m_conditionalExecutionHappened = true; m_currentSequenceCounter = sequenceCountersStart; @@ -533,7 +534,7 @@ void SMTChecker::checkCondition( ) { m_interface->push(); - m_interface->addAssertion(_condition); + m_interface->addAssertion(currentPathConditions() && _condition); vector expressionsToEvaluate; vector expressionNames; @@ -605,12 +606,12 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co return; m_interface->push(); - m_interface->addAssertion(expr(_condition)); + m_interface->addAssertion(currentPathConditions() && expr(_condition)); auto positiveResult = checkSatisifable(); m_interface->pop(); m_interface->push(); - m_interface->addAssertion(!expr(_condition)); + m_interface->addAssertion(currentPathConditions() && !expr(_condition)); auto negatedResult = checkSatisifable(); m_interface->pop(); @@ -828,3 +829,21 @@ smt::Expression SMTChecker::var(Declaration const& _decl) solAssert(m_variables.count(&_decl), ""); return m_variables.at(&_decl); } + +void SMTChecker::popPathCondition() +{ + solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty."); + m_pathConditions.pop_back(); +} + +void SMTChecker::pushPathCondition(smt::Expression const& _e) +{ + m_pathConditions.push_back(currentPathConditions() && _e); +} + +smt::Expression SMTChecker::currentPathConditions() +{ + if (m_pathConditions.size() == 0) + return smt::Expression(true); + return m_pathConditions.back(); +} diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index e7481cca..7f990b97 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -26,6 +26,7 @@ #include #include +#include namespace dev { @@ -145,6 +146,13 @@ private: /// The function takes one argument which is the "sequence number". smt::Expression var(Declaration const& _decl); + /// Adds a new path condition + void pushPathCondition(smt::Expression const& _e); + /// Remove the last path condition + void popPathCondition(); + /// Returns the conjunction of all path conditions or True if empty + smt::Expression currentPathConditions(); + std::shared_ptr m_interface; std::shared_ptr m_variableUsage; bool m_conditionalExecutionHappened = false; @@ -152,6 +160,7 @@ private: std::map m_nextFreeSequenceCounter; std::map m_expressions; std::map m_variables; + std::vector m_pathConditions; ErrorReporter& m_errorReporter; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 74c993e8..88487310 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -72,6 +72,11 @@ public: }, _trueValue.sort); } + static Expression implies(Expression _a, Expression _b) + { + return !std::move(_a) || std::move(_b); + } + friend Expression operator!(Expression _a) { return Expression("not", std::move(_a), Sort::Bool); -- cgit v1.2.3 From a1e296e3928b90c9a575e5e816abc566bba35493 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Wed, 13 Dec 2017 17:59:36 +0100 Subject: [SMTChecker] Helper functions to add an expression to the solver conjoined with or implied by the current path conditions --- libsolidity/formal/SMTChecker.cpp | 20 +++++++++++++++----- libsolidity/formal/SMTChecker.h | 4 ++++ 2 files changed, 19 insertions(+), 5 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index 61cb110e..d4887a3d 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -345,14 +345,14 @@ void SMTChecker::endVisit(FunctionCall const& _funCall) solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation"); - m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); + addPathImpliedExpression(expr(*args[0])); } else if (funType.kind() == FunctionType::Kind::Require) { solAssert(args.size() == 1, ""); solAssert(args[0]->annotation().type->category() == Type::Category::Bool, ""); checkBooleanNotConstant(*args[0], "Condition is always $VALUE."); - m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), expr(*args[0]))); + addPathImpliedExpression(expr(*args[0])); } } @@ -534,7 +534,7 @@ void SMTChecker::checkCondition( ) { m_interface->push(); - m_interface->addAssertion(currentPathConditions() && _condition); + addPathConjoinedExpression(_condition); vector expressionsToEvaluate; vector expressionNames; @@ -606,12 +606,12 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co return; m_interface->push(); - m_interface->addAssertion(currentPathConditions() && expr(_condition)); + addPathConjoinedExpression(expr(_condition)); auto positiveResult = checkSatisifable(); m_interface->pop(); m_interface->push(); - m_interface->addAssertion(currentPathConditions() && !expr(_condition)); + addPathConjoinedExpression(!expr(_condition)); auto negatedResult = checkSatisifable(); m_interface->pop(); @@ -847,3 +847,13 @@ smt::Expression SMTChecker::currentPathConditions() return smt::Expression(true); return m_pathConditions.back(); } + +void SMTChecker::addPathConjoinedExpression(smt::Expression const& _e) +{ + m_interface->addAssertion(currentPathConditions() && _e); +} + +void SMTChecker::addPathImpliedExpression(smt::Expression const& _e) +{ + m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), _e)); +} diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 7f990b97..539221cc 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -152,6 +152,10 @@ private: void popPathCondition(); /// Returns the conjunction of all path conditions or True if empty smt::Expression currentPathConditions(); + /// Conjoin the current path conditions with the given parameter and add to the solver + void addPathConjoinedExpression(smt::Expression const& _e); + /// Add to the solver: the given expression implied by the current path conditions + void addPathImpliedExpression(smt::Expression const& _e); std::shared_ptr m_interface; std::shared_ptr m_variableUsage; -- cgit v1.2.3 From e2828cfa61fd059e8458bb9422295eaf50e963b0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 15 Dec 2017 09:47:06 +0100 Subject: Favour if over switch in ABI coder. --- libsolidity/codegen/ABIFunctions.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 6648be06..00f59065 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -120,7 +120,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) Whiskers templ(R"( function (headStart, dataEnd) -> { - switch slt(sub(dataEnd, headStart), ) case 1 { revert(0, 0) } + if slt(sub(dataEnd, headStart), ) { revert(0, 0) } } )"); @@ -151,7 +151,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory) R"( { let offset := (add(headStart, )) - switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { revert(0, 0) } := (add(headStart, offset), dataEnd) } )" : @@ -1134,7 +1134,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from R"( // function (offset, end) -> array { - switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } let length := array := ((length)) let dst := array @@ -1169,7 +1169,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from else { string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize()); - templ("staticBoundsCheck", "switch gt(add(src, mul(length, " + baseEncodedSize + ")), end) case 1 { revert(0, 0) }"); + templ("staticBoundsCheck", "if gt(add(src, mul(length, " + baseEncodedSize + ")), end) { revert(0, 0) }"); templ("retrieveElementPos", "src"); templ("baseEncodedSize", baseEncodedSize); } @@ -1197,11 +1197,11 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) templ = R"( // function (offset, end) -> arrayPos, length { - switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } length := calldataload(offset) - switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { revert(0, 0) } arrayPos := add(offset, 0x20) - switch gt(add(arrayPos, mul(, )), end) case 1 { revert(0, 0) } + if gt(add(arrayPos, mul(, )), end) { revert(0, 0) } } )"; else @@ -1209,7 +1209,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type) // function (offset, end) -> arrayPos { arrayPos := offset - switch gt(add(arrayPos, mul(, )), end) case 1 { revert(0, 0) } + if gt(add(arrayPos, mul(, )), end) { revert(0, 0) } } )"; Whiskers w{templ}; @@ -1235,13 +1235,13 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _ Whiskers templ( R"( function (offset, end) -> array { - switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) } + if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) } let length := (offset) array := ((length)) mstore(array, length) let src := add(offset, 0x20) let dst := add(array, 0x20) - switch gt(add(src, length), end) case 1 { revert(0, 0) } + if gt(add(src, length), end) { revert(0, 0) } (src, dst, length) } )" @@ -1268,7 +1268,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr Whiskers templ(R"( // function (headStart, end) -> value { - switch slt(sub(end, headStart), ) case 1 { revert(0, 0) } + if slt(sub(end, headStart), ) { revert(0, 0) } value := () <#members> { @@ -1296,7 +1296,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr dynamic ? R"( let offset := (add(headStart, )) - switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(offset, 0xffffffffffffffff) { revert(0, 0) } mstore(add(value, ), (add(headStart, offset), end)) )" : R"( @@ -1501,7 +1501,7 @@ string ABIFunctions::arrayAllocationSizeFunction(ArrayType const& _type) Whiskers w(R"( function (length) -> size { // Make sure we can allocate memory without overflow - switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { revert(0, 0) } size := } @@ -1620,7 +1620,7 @@ string ABIFunctions::allocationFunction() memPtr := mload() let newFreePtr := add(memPtr, size) // protect against overflow - switch or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) case 1 { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } mstore(, newFreePtr) } )") -- cgit v1.2.3 From add4cde68cd9b5c52db8a312a34591a8bb61d8fa Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Mon, 18 Dec 2017 11:40:06 +0000 Subject: Populate the sourceLocation field properly in standard JSON on errors --- libsolidity/interface/StandardCompiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index ad01821e..d44254ed 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -81,15 +81,15 @@ Json::Value formatErrorWithException( else message = _message; + Json::Value sourceLocation; if (location && location->sourceName) { - Json::Value sourceLocation = Json::objectValue; sourceLocation["file"] = *location->sourceName; sourceLocation["start"] = location->start; sourceLocation["end"] = location->end; } - return formatError(_warning, _type, _component, message, formattedMessage, location); + return formatError(_warning, _type, _component, message, formattedMessage, sourceLocation); } set requestedContractNames(Json::Value const& _outputSelection) -- cgit v1.2.3 From b588134840bf604ed4fa6031fb06a89cbbd13bcd Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 18 Dec 2017 17:31:27 +0100 Subject: [SMTChecker] Fix typo in the code (satisifable->satisfiable) --- libsolidity/formal/SMTChecker.cpp | 12 ++++++------ libsolidity/formal/SMTChecker.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index d4887a3d..a8529795 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -560,7 +560,7 @@ void SMTChecker::checkCondition( } smt::CheckResult result; vector values; - tie(result, values) = checkSatisifableAndGenerateModel(expressionsToEvaluate); + tie(result, values) = checkSatisfiableAndGenerateModel(expressionsToEvaluate); string conditionalComment; if (m_conditionalExecutionHappened) @@ -607,12 +607,12 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co m_interface->push(); addPathConjoinedExpression(expr(_condition)); - auto positiveResult = checkSatisifable(); + auto positiveResult = checkSatisfiable(); m_interface->pop(); m_interface->push(); addPathConjoinedExpression(!expr(_condition)); - auto negatedResult = checkSatisifable(); + auto negatedResult = checkSatisfiable(); m_interface->pop(); if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR) @@ -642,7 +642,7 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co } pair> -SMTChecker::checkSatisifableAndGenerateModel(vector const& _expressionsToEvaluate) +SMTChecker::checkSatisfiableAndGenerateModel(vector const& _expressionsToEvaluate) { smt::CheckResult result; vector values; @@ -672,9 +672,9 @@ SMTChecker::checkSatisifableAndGenerateModel(vector const& _exp return make_pair(result, values); } -smt::CheckResult SMTChecker::checkSatisifable() +smt::CheckResult SMTChecker::checkSatisfiable() { - return checkSatisifableAndGenerateModel({}).first; + return checkSatisfiableAndGenerateModel({}).first; } void SMTChecker::initializeLocalVariables(FunctionDefinition const& _function) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index 539221cc..d4c2cf6f 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -100,9 +100,9 @@ private: std::pair> - checkSatisifableAndGenerateModel(std::vector const& _expressionsToEvaluate); + checkSatisfiableAndGenerateModel(std::vector const& _expressionsToEvaluate); - smt::CheckResult checkSatisifable(); + smt::CheckResult checkSatisfiable(); void initializeLocalVariables(FunctionDefinition const& _function); void resetVariables(std::vector _variables); -- cgit v1.2.3 From ff9fdfac57d8807399b1b1ddd5c69a819349251f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 3 Jan 2018 11:34:48 +0000 Subject: Properly handle colons in file names within jsonio --- libsolidity/interface/StandardCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index d44254ed..7aa971c6 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -461,7 +461,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value contractsOutput = Json::objectValue; for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector()) { - size_t colon = contractName.find(':'); + size_t colon = contractName.rfind(':'); solAssert(colon != string::npos, ""); string file = contractName.substr(0, colon); string name = contractName.substr(colon + 1); -- cgit v1.2.3 From a0771691ff1a8ea8b2dda07ff50e48fc81a2a705 Mon Sep 17 00:00:00 2001 From: Federico Bond Date: Thu, 4 Jan 2018 07:24:39 -0300 Subject: Improve error message for wrong struct initialization (#3359) --- libsolidity/analysis/TypeChecker.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 090e5159..75d71925 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1551,8 +1551,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) { + bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + string msg = - "Wrong argument count for function call: " + + "Wrong argument count for " + + string(isStructConstructorCall ? "struct constructor" : "function call") + + ": " + toString(arguments.size()) + " arguments given but expected " + toString(parameterTypes.size()) + -- cgit v1.2.3 From 00692a4ff66551ae99920380cc4e43de377e63c7 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 4 Jan 2018 14:24:19 +0100 Subject: Reset source location after using inline assembly. --- libsolidity/codegen/CompilerContext.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'libsolidity') diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ce9c3b7f..ab10d7dd 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -347,6 +347,9 @@ void CompilerContext::appendInlineAssembly( solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block."); assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system); + + // Reset the source location to the one of the node (instead of the CODEGEN source location) + updateSourceLocation(); } FunctionDefinition const& CompilerContext::resolveVirtualFunction( -- cgit v1.2.3 From d0abc5359b24a27376cc1a4ba8d0b35e7ca036d2 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 18 Dec 2017 19:43:15 +0100 Subject: [SMTChecker] Variables are merged after branches (ite variables) --- libsolidity/formal/SMTChecker.cpp | 32 +++++++++++++++++++++++++------- libsolidity/formal/SMTChecker.h | 18 ++++++++++++------ 2 files changed, 37 insertions(+), 13 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index a8529795..a64024b3 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -91,15 +91,16 @@ bool SMTChecker::visit(IfStatement const& _node) checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE."); - visitBranch(_node.trueStatement(), expr(_node.condition())); + auto countersEndFalse = m_currentSequenceCounter; + auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition())); vector touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement()); if (_node.falseStatement()) { - visitBranch(*_node.falseStatement(), !expr(_node.condition())); + countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition())); touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement()); } - resetVariables(touchedVariables); + mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse); return false; } @@ -506,12 +507,12 @@ void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& m_interface->addAssertion(newValue(_variable) == _value); } -void SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) +SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition) { - visitBranch(_statement, &_condition); + return visitBranch(_statement, &_condition); } -void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) +SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition) { VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter; @@ -522,7 +523,8 @@ void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* popPathCondition(); m_conditionalExecutionHappened = true; - m_currentSequenceCounter = sequenceCountersStart; + std::swap(sequenceCountersStart, m_currentSequenceCounter); + return sequenceCountersStart; } void SMTChecker::checkCondition( @@ -702,6 +704,22 @@ void SMTChecker::resetVariables(vector _variables) } } +void SMTChecker::mergeVariables(vector const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse) +{ + set uniqueVars(_variables.begin(), _variables.end()); + for (auto const* decl: uniqueVars) + { + int trueCounter = _countersEndTrue.at(decl); + int falseCounter = _countersEndFalse.at(decl); + solAssert(trueCounter != falseCounter, ""); + m_interface->addAssertion(newValue(*decl) == smt::Expression::ite( + _condition, + valueAtSequence(*decl, trueCounter), + valueAtSequence(*decl, falseCounter)) + ); + } +} + bool SMTChecker::createVariable(VariableDeclaration const& _varDecl) { if (dynamic_cast(_varDecl.type().get())) diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index d4c2cf6f..b57f0f96 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -75,10 +75,14 @@ private: void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location); void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location); - // Visits the branch given by the statement, pushes and pops the SMT checker. - // @param _condition if present, asserts that this condition is true within the branch. - void visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr); - void visitBranch(Statement const& _statement, smt::Expression _condition); + /// Maps a variable to an SSA index. + using VariableSequenceCounters = std::map; + + /// Visits the branch given by the statement, pushes and pops the current path conditions. + /// @param _condition if present, asserts that this condition is true within the branch. + /// @returns the variable sequence counter after visiting the branch. + VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr); + VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression _condition); /// Check that a condition can be satisfied. void checkCondition( @@ -106,6 +110,10 @@ private: void initializeLocalVariables(FunctionDefinition const& _function); void resetVariables(std::vector _variables); + /// Given two different branches and the touched variables, + /// merge the touched variables into after-branch ite variables + /// using the branch condition as guard. + void mergeVariables(std::vector const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse); /// Tries to create an uninitialized variable and returns true on success. /// This fails if the type is not supported. bool createVariable(VariableDeclaration const& _varDecl); @@ -134,8 +142,6 @@ private: static smt::Expression minValue(IntegerType const& _t); static smt::Expression maxValue(IntegerType const& _t); - using VariableSequenceCounters = std::map; - /// Returns the expression corresponding to the AST node. Throws if the expression does not exist. smt::Expression expr(Expression const& _e); /// Creates the expression (value can be arbitrary) -- cgit v1.2.3 From 9e7e312fdfc2598d0bd43efc72738845bf2e3992 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 5 Jan 2018 13:24:07 +0000 Subject: Properly support library file names containing a colon (such as URLs). --- libsolidity/interface/StandardCompiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 7aa971c6..04f5bd25 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -193,7 +193,7 @@ Json::Value formatLinkReferences(std::map const& linkRefere for (auto const& ref: linkReferences) { string const& fullname = ref.second; - size_t colon = fullname.find(':'); + size_t colon = fullname.rfind(':'); solAssert(colon != string::npos, ""); string file = fullname.substr(0, colon); string name = fullname.substr(colon + 1); -- cgit v1.2.3 From a91393f4d74352be022b9a83fc3007881f770ed7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Thu, 4 Jan 2018 23:25:31 +0000 Subject: Support some restricted tokens (return, byte, address) as identifiers in Julia --- libsolidity/inlineasm/AsmParser.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 273e1d5c..9336e620 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -566,10 +566,16 @@ string Parser::expectAsmIdentifier() string name = currentLiteral(); if (m_julia) { - if (currentToken() == Token::Bool) + switch (currentToken()) { + case Token::Return: + case Token::Byte: + case Token::Address: + case Token::Bool: advance(); return name; + default: + break; } } else if (instructions().count(name)) -- cgit v1.2.3 From fcbdaa32b90cd7da8729411481d587151f484fa1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 6 Jan 2018 00:09:08 +0000 Subject: Simplify parseElementaryOperation in regards to special instructions --- libsolidity/inlineasm/AsmParser.cpp | 27 +++++++++++++++++---------- libsolidity/inlineasm/AsmParser.h | 2 +- 2 files changed, 18 insertions(+), 11 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 9336e620..20c7b2a5 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -123,7 +123,7 @@ assembly::Statement Parser::parseStatement() // Simple instruction (might turn into functional), // literal, // identifier (might turn into label or functional assignment) - ElementaryOperation elementary(parseElementaryOperation(false)); + ElementaryOperation elementary(parseElementaryOperation()); switch (currentToken()) { case Token::LParen: @@ -145,7 +145,7 @@ assembly::Statement Parser::parseStatement() do { expectToken(Token::Comma); - elementary = parseElementaryOperation(false); + elementary = parseElementaryOperation(); if (elementary.type() != typeid(assembly::Identifier)) fatalParserError("Variable name expected in multiple assignemnt."); assignment.variableNames.emplace_back(boost::get(elementary)); @@ -247,10 +247,11 @@ assembly::ForLoop Parser::parseForLoop() assembly::Expression Parser::parseExpression() { RecursionGuard recursionGuard(*this); - ElementaryOperation operation = parseElementaryOperation(true); + ElementaryOperation operation = parseElementaryOperation(); if (operation.type() == typeid(Instruction)) { Instruction const& instr = boost::get(operation); + // Enforce functional notation for instructions requiring multiple arguments. int args = instructionInfo(instr.instruction).args; if (args > 0 && currentToken() != Token::LParen) fatalParserError(string( @@ -260,11 +261,23 @@ assembly::Expression Parser::parseExpression() boost::lexical_cast(args) + " arguments)" )); + // Disallow instructions returning multiple values (and DUP/SWAP) as expression. + if ( + instructionInfo(instr.instruction).ret != 1 || + isDupInstruction(instr.instruction) || + isSwapInstruction(instr.instruction) + ) + fatalParserError( + "Instruction \"" + + instructionNames().at(instr.instruction) + + "\" not allowed in this context." + ); } if (currentToken() == Token::LParen) return parseCall(std::move(operation)); else if (operation.type() == typeid(Instruction)) { + // Instructions not taking arguments are allowed as expressions. Instruction& instr = boost::get(operation); return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; } @@ -317,7 +330,7 @@ std::map const& Parser::instructionNames() return s_instructionNames; } -Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePusher) +Parser::ElementaryOperation Parser::parseElementaryOperation() { RecursionGuard recursionGuard(*this); ElementaryOperation ret; @@ -341,12 +354,6 @@ Parser::ElementaryOperation Parser::parseElementaryOperation(bool _onlySinglePus if (!m_julia && instructions().count(literal)) { dev::solidity::Instruction const& instr = instructions().at(literal); - if (_onlySinglePusher) - { - InstructionInfo info = dev::solidity::instructionInfo(instr); - if (info.ret != 1) - fatalParserError("Instruction \"" + literal + "\" not allowed in this context."); - } ret = Instruction{location(), instr}; } else diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 44889a13..bd90bb43 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -70,7 +70,7 @@ protected: assembly::Expression parseExpression(); static std::map const& instructions(); static std::map const& instructionNames(); - ElementaryOperation parseElementaryOperation(bool _onlySinglePusher = false); + ElementaryOperation parseElementaryOperation(); VariableDeclaration parseVariableDeclaration(); FunctionDefinition parseFunctionDefinition(); assembly::Expression parseCall(ElementaryOperation&& _initialOp); -- cgit v1.2.3 From 124190336b0a70ea32d5f8ca0c4b364f1fc774d0 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 13 Dec 2017 14:40:54 +0100 Subject: Split inline assembly into loose and strict flavours. --- libsolidity/analysis/ReferencesResolver.cpp | 2 +- libsolidity/analysis/TypeChecker.cpp | 2 +- libsolidity/codegen/CompilerContext.cpp | 9 +++++++-- libsolidity/codegen/CompilerContext.h | 4 ++-- libsolidity/inlineasm/AsmAnalysis.cpp | 17 ++++++++-------- libsolidity/inlineasm/AsmAnalysis.h | 6 +++--- libsolidity/inlineasm/AsmDataForward.h | 7 +++++++ libsolidity/inlineasm/AsmParser.cpp | 31 +++++++++++++++++------------ libsolidity/inlineasm/AsmParser.h | 8 ++++++-- libsolidity/interface/AssemblyStack.cpp | 21 +++++++++++++++++-- 10 files changed, 73 insertions(+), 34 deletions(-) (limited to 'libsolidity') diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 9eee16af..540ffaf5 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -207,7 +207,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) // Will be re-generated later with correct information assembly::AsmAnalysisInfo analysisInfo; - assembly::AsmAnalyzer(analysisInfo, errorsIgnored, false, resolver).analyze(_inlineAssembly.operations()); + assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations()); return false; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 75d71925..191f78e9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -873,7 +873,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) assembly::AsmAnalyzer analyzer( *_inlineAssembly.annotation().analysisInfo, m_errorReporter, - false, + assembly::AsmFlavour::Loose, identifierAccess ); if (!analyzer.analyze(_inlineAssembly.operations())) diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index ab10d7dd..7a88475a 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -319,14 +319,19 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared(CharStream(_assembly), "--CODEGEN--"); - auto parserResult = assembly::Parser(errorReporter).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner); #ifdef SOL_OUTPUT_ASM cout << assembly::AsmPrinter()(*parserResult) << endl; #endif assembly::AsmAnalysisInfo analysisInfo; bool analyzerResult = false; if (parserResult) - analyzerResult = assembly::AsmAnalyzer(analysisInfo, errorReporter, false, identifierAccess.resolve).analyze(*parserResult); + analyzerResult = assembly::AsmAnalyzer( + analysisInfo, + errorReporter, + assembly::AsmFlavour::Strict, + identifierAccess.resolve + ).analyze(*parserResult); if (!parserResult || !errorReporter.errors().empty() || !analyzerResult) { string message = diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 7743fd3f..0e8b639c 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -187,8 +187,8 @@ public: CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; } - /// Appends inline assembly. @a _replacements are string-matching replacements that are performed - /// prior to parsing the inline assembly. + /// Appends inline assembly (strict mode). + /// @a _replacements are string-matching replacements that are performed prior to parsing the inline assembly. /// @param _localVariables assigns stack positions to variables with the last one being the stack top /// @param _system if true, this is a "system-level" assembly where all functions use named labels. void appendInlineAssembly( diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 17b7cce0..2d6e58de 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -54,7 +54,7 @@ bool AsmAnalyzer::analyze(Block const& _block) bool AsmAnalyzer::operator()(Label const& _label) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); m_info.stackHeightInfo[&_label] = m_stackHeight; warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); return true; @@ -62,7 +62,7 @@ bool AsmAnalyzer::operator()(Label const& _label) bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); auto const& info = instructionInfo(_instruction.instruction); m_stackHeight += info.ret - info.args; m_info.stackHeightInfo[&_instruction] = m_stackHeight; @@ -141,7 +141,7 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier) bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { - solAssert(!m_julia, ""); + solAssert(m_flavour != AsmFlavour::IULIA, ""); bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) if (!expectExpression(arg)) @@ -157,17 +157,18 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement) { -// size_t initialStackHeight = m_stackHeight; + size_t initialStackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, _statement.expression); -// if (!expectDeposit(0, initialStackHeight, _statement.location)) -// success = false; + if (m_flavour != AsmFlavour::Loose) + if (!expectDeposit(0, initialStackHeight, _statement.location)) + success = false; m_info.stackHeightInfo[&_statement] = m_stackHeight; return success; } bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment) { - solAssert(!m_julia, ""); + solAssert(m_flavour == AsmFlavour::Loose, ""); bool success = checkAssignment(_assignment.variableName, size_t(-1)); m_info.stackHeightInfo[&_assignment] = m_stackHeight; return success; @@ -507,7 +508,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) } void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) { - if (!m_julia) + if (m_flavour != AsmFlavour::IULIA) return; if (!builtinTypes.count(type)) diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h index 00a33db3..7a81dbf8 100644 --- a/libsolidity/inlineasm/AsmAnalysis.h +++ b/libsolidity/inlineasm/AsmAnalysis.h @@ -54,9 +54,9 @@ public: explicit AsmAnalyzer( AsmAnalysisInfo& _analysisInfo, ErrorReporter& _errorReporter, - bool _julia = false, + AsmFlavour _flavour = AsmFlavour::Loose, julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver() - ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_julia(_julia) {} + ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {} bool analyze(assembly::Block const& _block); @@ -97,7 +97,7 @@ private: std::set m_activeVariables; AsmAnalysisInfo& m_info; ErrorReporter& m_errorReporter; - bool m_julia = false; + AsmFlavour m_flavour = AsmFlavour::Loose; }; } diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h index 317e257c..3a9600fe 100644 --- a/libsolidity/inlineasm/AsmDataForward.h +++ b/libsolidity/inlineasm/AsmDataForward.h @@ -53,6 +53,13 @@ struct TypedName; using Expression = boost::variant; using Statement = boost::variant; +enum class AsmFlavour +{ + Loose, // no types, EVM instructions as function, jumps and direct stack manipulations + Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations + IULIA // same as Strict mode with types +}; + } } } diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 20c7b2a5..5983d7ff 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -103,14 +103,14 @@ assembly::Statement Parser::parseStatement() return parseForLoop(); case Token::Assign: { - if (m_julia) + if (m_flavour != AsmFlavour::Loose) break; assembly::StackAssignment assignment = createWithLocation(); advance(); expectToken(Token::Colon); assignment.variableName.location = location(); assignment.variableName.name = currentLiteral(); - if (!m_julia && instructions().count(assignment.variableName.name)) + if (instructions().count(assignment.variableName.name)) fatalParserError("Identifier expected, got instruction name."); assignment.location.end = endPosition(); expectToken(Token::Identifier); @@ -170,7 +170,7 @@ assembly::Statement Parser::parseStatement() if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { assembly::Assignment assignment = createWithLocation(identifier.location); - if (!m_julia && instructions().count(identifier.name)) + if (m_flavour != AsmFlavour::IULIA && instructions().count(identifier.name)) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); @@ -181,7 +181,7 @@ assembly::Statement Parser::parseStatement() else { // label - if (m_julia) + if (m_flavour != AsmFlavour::Loose) fatalParserError("Labels are not supported."); Label label = createWithLocation