diff options
Diffstat (limited to 'libsolidity')
25 files changed, 285 insertions, 133 deletions
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index f7c1a390..99612c40 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -6,9 +6,9 @@ find_package(Z3 QUIET) if (${Z3_FOUND}) include_directories(${Z3_INCLUDE_DIR}) add_definitions(-DHAVE_Z3) - message("Z3 SMT solver FOUND.") + message("Z3 SMT solver found. This enables optional SMT checking.") else() - message("Z3 SMT solver NOT found.") + message("Z3 SMT solver NOT found. Optional SMT checking will not be available. Please install Z3 if it is desired.") list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/formal/Z3Interface.cpp") endif() diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 7057eab7..bc3b7cf1 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -22,38 +22,55 @@ #include <libsolidity/analysis/ConstantEvaluator.h> #include <libsolidity/ast/AST.h> +#include <libsolidity/interface/ErrorReporter.h> 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<RationalNumberType const*>(subType.get())) - BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression.")); + m_errorReporter.fatalTypeError(_operation.subExpression().location(), "Invalid constant expression."); TypePointer t = subType->unaryOperatorResult(_operation.getOperator()); _operation.annotation().type = t; } +/// 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<RationalNumberType const*>(leftType.get())) - BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression.")); + m_errorReporter.fatalTypeError(_operation.leftExpression().location(), "Invalid constant expression."); if (!dynamic_cast<RationalNumberType const*>(rightType.get())) - BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression.")); + m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression."); TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType); - if (Token::isCompareOp(_operation.getOperator())) - commonType = make_shared<BoolType>(); - _operation.annotation().type = commonType; + if (!commonType) + { + m_errorReporter.typeError( + _operation.location(), + "Operator " + + string(Token::toString(_operation.getOperator())) + + " not compatible with types " + + leftType->toString() + + " and " + + rightType->toString() + ); + commonType = leftType; + } + _operation.annotation().commonType = commonType; + _operation.annotation().type = + Token::isCompareOp(_operation.getOperator()) ? + make_shared<BoolType>() : + commonType; } void ConstantEvaluator::endVisit(Literal const& _literal) { _literal.annotation().type = Type::forLiteral(_literal); if (!_literal.annotation().type) - BOOST_THROW_EXCEPTION(_literal.createTypeError("Invalid literal value.")); + m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); } diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 9ec04ebe..90bceb5d 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -29,6 +29,7 @@ namespace dev namespace solidity { +class ErrorReporter; class TypeChecker; /** @@ -37,13 +38,18 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: - ConstantEvaluator(Expression const& _expr) { _expr.accept(*this); } + ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter): + m_errorReporter(_errorReporter) + { + _expr.accept(*this); + } private: virtual void endVisit(BinaryOperation const& _operation); virtual void endVisit(UnaryOperation const& _operation); virtual void endVisit(Literal const& _literal); + ErrorReporter& m_errorReporter; }; } diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 523e7176..5d010693 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -647,10 +647,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio bool warnAboutShadowing = true; // Do not warn about shadowing for structs and enums because their members are - // not accessible without prefixes. + // not accessible without prefixes. Also do not warn about event parameters + // because they don't participate in any proper scope. if ( dynamic_cast<StructDefinition const*>(m_currentScope) || - dynamic_cast<EnumDefinition const*>(m_currentScope) + dynamic_cast<EnumDefinition const*>(m_currentScope) || + dynamic_cast<EventDefinition const*>(m_currentScope) ) warnAboutShadowing = false; // Do not warn about the constructor shadowing the contract. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 20016112..f22c95cc 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -147,7 +147,7 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) if (Expression const* length = _typeName.length()) { if (!length->annotation().type) - ConstantEvaluator e(*length); + ConstantEvaluator e(*length, m_errorReporter); auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get()); if (!lengthType || !lengthType->mobileType()) fatalTypeError(length->location(), "Invalid array length, expected integer literal."); @@ -298,11 +298,19 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) { typeLoc = DataLocation::Storage; if (_variable.isLocalVariable()) - m_errorReporter.warning( - _variable.location(), - "Variable is declared as a storage pointer. " - "Use an explicit \"storage\" keyword to silence this warning." - ); + { + if (_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + typeError( + _variable.location(), + "Storage location must be specified as either \"memory\" or \"storage\"." + ); + else + m_errorReporter.warning( + _variable.location(), + "Variable is declared as a storage pointer. " + "Use an explicit \"storage\" keyword to silence this warning." + ); + } } } else diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index ffa538b6..bd8ee597 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -150,10 +150,18 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess) if (_memberAccess.memberName() == "callcode") if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) if (type->kind() == FunctionType::Kind::BareCallCode) - m_errorReporter.warning( - _memberAccess.location(), - "\"callcode\" has been deprecated in favour of \"delegatecall\"." - ); + { + if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError( + _memberAccess.location(), + "\"callcode\" has been deprecated in favour of \"delegatecall\"." + ); + else + m_errorReporter.warning( + _memberAccess.location(), + "\"callcode\" has been deprecated in favour of \"delegatecall\"." + ); + } if (m_constructor && m_currentContract) if (ContractType const* type = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type.get())) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b2a88059..746e762e 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -595,8 +595,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) { if (_function.isImplemented()) m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation."); - if (_function.visibility() < FunctionDefinition::Visibility::Public) - m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private."); + if (_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + { + if (_function.visibility() != FunctionDefinition::Visibility::External) + m_errorReporter.typeError(_function.location(), "Functions in interfaces must be declared external."); + } + else + { + if (_function.visibility() < FunctionDefinition::Visibility::Public) + m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private."); + } if (_function.isConstructor()) m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces."); } @@ -1285,6 +1293,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple) { components[i]->accept(*this); types.push_back(type(*components[i])); + + // Note: code generation will visit each of the expression even if they are not assigned from. + if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1) + if (!dynamic_cast<RationalNumberType const&>(*types[i]).mobileType()) + m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number."); + if (_tuple.isInlineArray()) solAssert(!!types[i], "Inline array cannot have empty components"); if (_tuple.isInlineArray()) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index a805322b..1048b610 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -22,7 +22,6 @@ #include <libsolidity/ast/AST.h> #include <libsolidity/ast/ASTVisitor.h> -#include <libsolidity/interface/Exceptions.h> #include <libsolidity/ast/AST_accept.h> #include <libdevcore/SHA3.h> @@ -73,11 +72,6 @@ ASTAnnotation& ASTNode::annotation() const return *m_annotation; } -Error ASTNode::createTypeError(string const& _description) const -{ - return Error(Error::Type::TypeError) << errinfo_sourceLocation(location()) << errinfo_comment(_description); -} - SourceUnitAnnotation& SourceUnit::annotation() const { if (!m_annotation) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 75b8e946..733e7c78 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -26,7 +26,6 @@ #include <libsolidity/ast/ASTForward.h> #include <libsolidity/parsing/Token.h> #include <libsolidity/ast/Types.h> -#include <libsolidity/interface/Exceptions.h> #include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTEnums.h> @@ -89,10 +88,6 @@ public: /// Returns the source code location of this node. SourceLocation const& location() const { return m_location; } - /// Creates a @ref TypeError exception and decorates it with the location of the node and - /// the given description - Error createTypeError(std::string const& _description) const; - ///@todo make this const-safe by providing a different way to access the annotation virtual ASTAnnotation& annotation() const; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 15735368..46675e51 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -57,6 +57,7 @@ class UserDefinedTypeName; class FunctionTypeName; class Mapping; class ArrayTypeName; +class InlineAssembly; class Statement; class Block; class PlaceholderStatement; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a3cbe50a..ee5f462b 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -1616,9 +1616,10 @@ string ContractType::canonicalName() const return m_contract.annotation().canonicalName; } -MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) const +MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _contract) const { MemberList::MemberMap members; + solAssert(_contract, ""); if (m_super) { // add the most derived of all functions which are visible in derived contracts @@ -1660,7 +1661,9 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con &it.second->declaration() )); } - addNonConflictingAddressMembers(members); + // In 0.5.0 address members are not populated into the contract. + if (!_contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + addNonConflictingAddressMembers(members); return members; } @@ -2022,7 +2025,7 @@ unsigned EnumType::memberValue(ASTString const& _member) const return index; ++index; } - BOOST_THROW_EXCEPTION(m_enum.createTypeError("Requested unknown enum value ." + _member)); + solAssert(false, "Requested unknown enum value " + _member); } bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 756148e7..080be359 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -87,7 +87,7 @@ string ABIFunctions::tupleEncoder( ); elementTempl("values", valueNames); elementTempl("pos", to_string(headPos)); - elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, false)); + elementTempl("abiEncode", abiEncodingFunction(*_givenTypes[i], *_targetTypes[i], _encodeAsLibraryTypes, true)); encodeElements += elementTempl.render(); headPos += dynamic ? 0x20 : _targetTypes[i]->calldataEncodedSize(); } @@ -371,7 +371,7 @@ string ABIFunctions::abiEncodingFunction( Type const& _from, Type const& _to, bool _encodeAsLibraryTypes, - bool _compacted + bool _fromStack ) { solUnimplementedAssert( @@ -415,7 +415,7 @@ string ABIFunctions::abiEncodingFunction( dynamic_cast<FunctionType const&>(_from), to, _encodeAsLibraryTypes, - _compacted + _fromStack ); solAssert(_from.sizeOnStack() == 1, ""); @@ -542,7 +542,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( mstore(pos, sub(tail, headStart)) tail := <encodeToMemoryFun>(<arrayElementAccess>, tail) srcPtr := <nextArrayElement>(srcPtr) - pos := add(pos, <elementEncodedSize>) + pos := add(pos, 0x20) } pos := tail <assignEnd> @@ -580,7 +580,7 @@ string ABIFunctions::abiEncodingFunctionSimpleArray( *_from.baseType(), *_to.baseType(), _encodeAsLibraryTypes, - true + false )); templ("arrayElementAccess", inMemory ? "mload(srcPtr)" : _from.baseType()->isValueType() ? "sload(srcPtr)" : "srcPtr" ); templ("nextArrayElement", nextArrayElementFunction(_from)); @@ -729,7 +729,7 @@ string ABIFunctions::abiEncodingFunctionCompactStorageArray( *_from.baseType(), *_to.baseType(), _encodeAsLibraryTypes, - true + false ); templ("encodeToMemoryFun", encodeToMemoryFun); std::vector<std::map<std::string, std::string>> items(itemsPerSlot); @@ -925,7 +925,7 @@ string ABIFunctions::abiEncodingFunctionFunctionType( FunctionType const& _from, Type const& _to, bool _encodeAsLibraryTypes, - bool _compacted + bool _fromStack ) { solAssert(_from.kind() == FunctionType::Kind::External, ""); @@ -936,35 +936,31 @@ string ABIFunctions::abiEncodingFunctionFunctionType( _from.identifier() + "_to_" + _to.identifier() + - (_compacted ? "_compacted" : "") + + (_fromStack ? "_fromStack" : "") + (_encodeAsLibraryTypes ? "_library" : ""); - if (_compacted) - { + if (_fromStack) return createFunction(functionName, [&]() { return Whiskers(R"( - function <functionName>(addr_and_function_id, pos) { - mstore(pos, <cleanExtFun>(addr_and_function_id)) + function <functionName>(addr, function_id, pos) { + mstore(pos, <combineExtFun>(addr, function_id)) } )") ("functionName", functionName) - ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction()) + ("combineExtFun", combineExternalFunctionIdFunction()) .render(); }); - } else - { return createFunction(functionName, [&]() { return Whiskers(R"( - function <functionName>(addr, function_id, pos) { - mstore(pos, <combineExtFun>(addr, function_id)) + function <functionName>(addr_and_function_id, pos) { + mstore(pos, <cleanExtFun>(addr_and_function_id)) } )") ("functionName", functionName) - ("combineExtFun", combineExternalFunctionIdFunction()) + ("cleanExtFun", cleanupCombinedExternalFunctionIdFunction()) .render(); }); - } } string ABIFunctions::copyToMemoryFunction(bool _fromCalldata) @@ -1212,10 +1208,7 @@ size_t ABIFunctions::headSize(TypePointers const& _targetTypes) if (t->isDynamicallyEncoded()) headSize += 0x20; else - { - solAssert(t->calldataEncodedSize() > 0, ""); headSize += t->calldataEncodedSize(); - } } return headSize; diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index de2a140a..e61f68bc 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -89,13 +89,13 @@ private: /// @returns the name of the ABI encoding function with the given type /// and queues the generation of the function to the requested functions. - /// @param _compacted if true, the input value was just loaded from storage + /// @param _fromStack if false, the input value was just loaded from storage /// or memory and thus might be compacted into a single slot (depending on the type). std::string abiEncodingFunction( Type const& _givenType, Type const& _targetType, bool _encodeAsLibraryTypes, - bool _compacted + bool _fromStack ); /// Part of @a abiEncodingFunction for array target type and given calldata array. std::string abiEncodingFunctionCalldataArray( @@ -143,7 +143,7 @@ private: FunctionType const& _from, Type const& _to, bool _encodeAsLibraryTypes, - bool _compacted + bool _fromStack ); /// @returns a function that copies raw bytes of dynamic length from calldata diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index e17188c2..ce8cbb5f 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -291,8 +291,11 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord CompilerUtils utils(m_context); unsigned baseSize = 1; if (!_sourceType.isByteArray()) + { // We always pad the elements, regardless of _padToWordBoundaries. baseSize = _sourceType.baseType()->calldataEncodedSize(); + solAssert(baseSize >= 0x20, ""); + } if (_sourceType.location() == DataLocation::CallData) { diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 429db532..74565ae4 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -251,13 +251,10 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac FunctionDefinition const* fallback = _contract.fallbackFunction(); eth::AssemblyItem notFound = m_context.newTag(); - // shortcut messages without data if we have many functions in order to be able to receive - // ether with constant gas - if (interfaceFunctions.size() > 5 || fallback) - { - m_context << Instruction::CALLDATASIZE << Instruction::ISZERO; - m_context.appendConditionalJumpTo(notFound); - } + // directly jump to fallback if the data is too short to contain a function selector + // also guards against short data + m_context << u256(4) << Instruction::CALLDATASIZE << Instruction::LT; + m_context.appendConditionalJumpTo(notFound); // retrieve the function signature hash from the calldata if (!interfaceFunctions.empty()) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c2bf0f5c..bb8c4a94 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -858,8 +858,15 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::MSTORE; // Stack: memptr requested_length // update free memory pointer - m_context << Instruction::DUP1 << arrayType.baseType()->memoryHeadSize(); - m_context << Instruction::MUL << u256(32) << Instruction::ADD; + m_context << Instruction::DUP1; + // Stack: memptr requested_length requested_length + if (arrayType.isByteArray()) + // Round up to multiple of 32 + m_context << u256(31) << Instruction::ADD << u256(31) << Instruction::NOT << Instruction::AND; + else + m_context << arrayType.baseType()->memoryHeadSize() << Instruction::MUL; + // stacK: memptr requested_length data_size + m_context << u256(32) << Instruction::ADD; m_context << Instruction::DUP3 << Instruction::ADD; utils().storeFreeMemoryPointer(); // Stack: memptr requested_length @@ -1714,6 +1721,9 @@ void ExpressionCompiler::appendExternalFunctionCall( if (_functionType.gasSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); + else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050)) + // Send all gas (requires tangerine whistle EVM) + m_context << Instruction::GAS; else { // send all gas except the amount needed to execute "SUB" and "CALL" diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp index fd78e578..2d2f05ec 100644 --- a/libsolidity/formal/SMTChecker.cpp +++ b/libsolidity/formal/SMTChecker.cpp @@ -234,6 +234,16 @@ void SMTChecker::endVisit(BinaryOperation const& _op) void SMTChecker::endVisit(FunctionCall const& _funCall) { + solAssert(_funCall.annotation().kind != FunctionCallKind::Unset, ""); + if (_funCall.annotation().kind != FunctionCallKind::FunctionCall) + { + m_errorReporter.warning( + _funCall.location(), + "Assertion checker does not yet implement this expression." + ); + return; + } + FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type); std::vector<ASTPointer<Expression const>> const args = _funCall.arguments(); @@ -484,10 +494,10 @@ void SMTChecker::createVariable(VariableDeclaration const& _varDecl, bool _setTo { solAssert(m_currentSequenceCounter.count(&_varDecl) == 0, ""); solAssert(m_nextFreeSequenceCounter.count(&_varDecl) == 0, ""); - solAssert(m_Variables.count(&_varDecl) == 0, ""); + solAssert(m_variables.count(&_varDecl) == 0, ""); m_currentSequenceCounter[&_varDecl] = 0; m_nextFreeSequenceCounter[&_varDecl] = 1; - m_Variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); + m_variables.emplace(&_varDecl, m_interface->newFunction(uniqueSymbol(_varDecl), smt::Sort::Int, smt::Sort::Int)); setValue(_varDecl, _setToZero); } else @@ -556,7 +566,7 @@ smt::Expression SMTChecker::maxValue(IntegerType const& _t) smt::Expression SMTChecker::expr(Expression const& _e) { - if (!m_Expressions.count(&_e)) + if (!m_expressions.count(&_e)) { solAssert(_e.annotation().type, ""); switch (_e.annotation().type->category()) @@ -565,24 +575,24 @@ smt::Expression SMTChecker::expr(Expression const& _e) { if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(_e.annotation().type.get())) solAssert(!rational->isFractional(), ""); - m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; } case Type::Category::Integer: - m_Expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); + m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e))); break; case Type::Category::Bool: - m_Expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); + m_expressions.emplace(&_e, m_interface->newBool(uniqueSymbol(_e))); break; default: solAssert(false, "Type not implemented."); } } - return m_Expressions.at(&_e); + return m_expressions.at(&_e); } smt::Expression SMTChecker::var(Declaration const& _decl) { - solAssert(m_Variables.count(&_decl), ""); - return m_Variables.at(&_decl); + solAssert(m_variables.count(&_decl), ""); + return m_variables.at(&_decl); } diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h index d23fd201..faaac639 100644 --- a/libsolidity/formal/SMTChecker.h +++ b/libsolidity/formal/SMTChecker.h @@ -103,8 +103,8 @@ private: std::shared_ptr<smt::SolverInterface> m_interface; std::map<Declaration const*, int> m_currentSequenceCounter; std::map<Declaration const*, int> m_nextFreeSequenceCounter; - std::map<Expression const*, smt::Expression> m_Expressions; - std::map<Declaration const*, smt::Expression> m_Variables; + std::map<Expression const*, smt::Expression> m_expressions; + std::map<Declaration const*, smt::Expression> m_variables; ErrorReporter& m_errorReporter; FunctionDefinition const* m_currentFunction = nullptr; diff --git a/libsolidity/formal/SMTLib2Interface.cpp b/libsolidity/formal/SMTLib2Interface.cpp index cbd766fb..c627057a 100644 --- a/libsolidity/formal/SMTLib2Interface.cpp +++ b/libsolidity/formal/SMTLib2Interface.cpp @@ -64,6 +64,8 @@ void SMTLib2Interface::pop() Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codomain) { + solAssert(!m_variables.count(_name), ""); + m_variables[_name] = SMTVariableType::Function; write( "(declare-fun |" + _name + @@ -78,12 +80,16 @@ Expression SMTLib2Interface::newFunction(string _name, Sort _domain, Sort _codom Expression SMTLib2Interface::newInteger(string _name) { + solAssert(!m_variables.count(_name), ""); + m_variables[_name] = SMTVariableType::Integer; write("(declare-const |" + _name + "| Int)"); return SolverInterface::newInteger(move(_name)); } Expression SMTLib2Interface::newBool(string _name) { + solAssert(!m_variables.count(_name), ""); + m_variables[_name] = SMTVariableType::Bool; write("(declare-const |" + _name + "| Bool)"); return SolverInterface::newBool(std::move(_name)); } @@ -145,7 +151,8 @@ string SMTLib2Interface::checkSatAndGetValuesCommand(vector<Expression> const& _ for (size_t i = 0; i < _expressionsToEvaluate.size(); i++) { auto const& e = _expressionsToEvaluate.at(i); - // TODO they don't have to be ints... + solAssert(m_variables.count(e.name), ""); + solAssert(m_variables[e.name] == SMTVariableType::Integer, ""); command += "(declare-const |EVALEXPR_" + to_string(i) + "| Int)\n"; command += "(assert (= |EVALEXPR_" + to_string(i) + "| " + toSExpr(e) + "))\n"; } diff --git a/libsolidity/formal/SMTLib2Interface.h b/libsolidity/formal/SMTLib2Interface.h index 63188acd..e827449f 100644 --- a/libsolidity/formal/SMTLib2Interface.h +++ b/libsolidity/formal/SMTLib2Interface.h @@ -68,6 +68,14 @@ private: ReadCallback::Callback m_queryCallback; std::vector<std::string> m_accumulatedOutput; + + enum class SMTVariableType { + Function, + Integer, + Bool + }; + + std::map<std::string,SMTVariableType> m_variables; }; } diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp index 0ceed3a7..6111b2c8 100644 --- a/libsolidity/formal/Z3Interface.cpp +++ b/libsolidity/formal/Z3Interface.cpp @@ -73,28 +73,37 @@ void Z3Interface::addAssertion(Expression const& _expr) pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _expressionsToEvaluate) { CheckResult result; - switch (m_solver.check()) + vector<string> values; + try { - case z3::check_result::sat: - result = CheckResult::SATISFIABLE; - break; - case z3::check_result::unsat: - result = CheckResult::UNSATISFIABLE; - break; - case z3::check_result::unknown: - result = CheckResult::UNKNOWN; - break; - default: - solAssert(false, ""); + switch (m_solver.check()) + { + case z3::check_result::sat: + result = CheckResult::SATISFIABLE; + break; + case z3::check_result::unsat: + result = CheckResult::UNSATISFIABLE; + break; + case z3::check_result::unknown: + result = CheckResult::UNKNOWN; + break; + default: + solAssert(false, ""); + } + + if (result != CheckResult::UNSATISFIABLE) + { + z3::model m = m_solver.get_model(); + for (Expression const& e: _expressionsToEvaluate) + values.push_back(toString(m.eval(toZ3Expr(e)))); + } } - - vector<string> values; - if (result != CheckResult::UNSATISFIABLE) + catch (z3::exception const&) { - z3::model m = m_solver.get_model(); - for (Expression const& e: _expressionsToEvaluate) - values.push_back(toString(m.eval(toZ3Expr(e)))); + result = CheckResult::ERROR; + values.clear(); } + return make_pair(result, values); } @@ -118,8 +127,7 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr) {">=", 2}, {"+", 2}, {"-", 2}, - {"*", 2}, - {">=", 2} + {"*", 2} }; string const& n = _expr.name; if (m_functions.count(n)) diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 51544f8a..5713256a 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -252,6 +252,14 @@ bool CompilerStack::parseAndAnalyze() return parse() && analyze(); } +bool CompilerStack::isRequestedContract(ContractDefinition const& _contract) const +{ + return + m_requestedContractNames.empty() || + m_requestedContractNames.count(_contract.fullyQualifiedName()) || + m_requestedContractNames.count(_contract.name()); +} + bool CompilerStack::compile() { if (m_stackState < AnalysisSuccessful) @@ -262,7 +270,8 @@ bool CompilerStack::compile() for (Source const* source: m_sourceOrder) for (ASTPointer<ASTNode> const& node: source->ast->nodes()) if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) - compileContract(*contract, compiledContracts); + if (isRequestedContract(*contract)) + compileContract(*contract, compiledContracts); this->link(); m_stackState = CompilationSuccessful; return true; @@ -738,22 +747,32 @@ void CompilerStack::compileContract( } } +string const CompilerStack::lastContractName() const +{ + if (m_contracts.empty()) + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); + // try to find some user-supplied contract + string contractName; + for (auto const& it: m_sources) + for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) + if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) + contractName = contract->fullyQualifiedName(); + return contractName; +} + CompilerStack::Contract const& CompilerStack::contract(string const& _contractName) const { if (m_contracts.empty()) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("No compiled contracts found.")); - string contractName = _contractName; - if (_contractName.empty()) - // try to find some user-supplied contract - for (auto const& it: m_sources) - for (ASTPointer<ASTNode> const& node: it.second.ast->nodes()) - if (auto contract = dynamic_cast<ContractDefinition const*>(node.get())) - contractName = contract->fullyQualifiedName(); - auto it = m_contracts.find(contractName); + + auto it = m_contracts.find(_contractName); + if (it != m_contracts.end()) + return it->second; + // To provide a measure of backward-compatibility, if a contract is not located by its // fully-qualified name, a lookup will be attempted purely on the contract's name to see // if anything will satisfy. - if (it == m_contracts.end() && contractName.find(":") == string::npos) + if (_contractName.find(":") == string::npos) { for (auto const& contractEntry: m_contracts) { @@ -764,12 +783,13 @@ CompilerStack::Contract const& CompilerStack::contract(string const& _contractNa string foundName; getline(ss, source, ':'); getline(ss, foundName, ':'); - if (foundName == contractName) return contractEntry.second; + if (foundName == _contractName) + return contractEntry.second; } - // If we get here, both lookup methods failed. - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract " + _contractName + " not found.")); } - return it->second; + + // If we get here, both lookup methods failed. + BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Contract \"" + _contractName + "\" not found.")); } CompilerStack::Source const& CompilerStack::source(string const& _sourceName) const diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index f1bbae47..b377b3aa 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -116,6 +116,13 @@ public: m_optimizeRuns = _runs; } + /// Sets the list of requested contract names. If empty, no filtering is performed and every contract + /// found in the supplied sources is compiled. Names are cleared iff @a _contractNames is missing. + void setRequestedContractNames(std::set<std::string> const& _contractNames = std::set<std::string>{}) + { + m_requestedContractNames = _contractNames; + } + /// @arg _metadataLiteralSources When true, store sources as literals in the contract metadata. void useMetadataLiteralSources(bool _metadataLiteralSources) { m_metadataLiteralSources = _metadataLiteralSources; } @@ -148,10 +155,10 @@ public: std::map<std::string, unsigned> sourceIndices() const; /// @returns the previously used scanner, useful for counting lines during error reporting. - Scanner const& scanner(std::string const& _sourceName = "") const; + Scanner const& scanner(std::string const& _sourceName) const; /// @returns the parsed source unit with the supplied name. - SourceUnit const& ast(std::string const& _sourceName = "") const; + SourceUnit const& ast(std::string const& _sourceName) const; /// Helper function for logs printing. Do only use in error cases, it's quite expensive. /// line and columns are numbered starting from 1 with following order: @@ -161,48 +168,51 @@ public: /// @returns a list of the contract names in the sources. std::vector<std::string> contractNames() const; + /// @returns the name of the last contract. + std::string const lastContractName() const; + /// @returns either the contract's name or a mixture of its name and source file, sanitized for filesystem use std::string const filesystemFriendlyName(std::string const& _contractName) const; /// @returns the assembled object for a contract. - eth::LinkerObject const& object(std::string const& _contractName = "") const; + eth::LinkerObject const& object(std::string const& _contractName) const; /// @returns the runtime object for the contract. - eth::LinkerObject const& runtimeObject(std::string const& _contractName = "") const; + eth::LinkerObject const& runtimeObject(std::string const& _contractName) const; /// @returns the bytecode of a contract that uses an already deployed contract via DELEGATECALL. /// The returned bytes will contain a sequence of 20 bytes of the format "XXX...XXX" which have to /// substituted by the actual address. Note that this sequence starts end ends in three X /// characters but can contain anything in between. - eth::LinkerObject const& cloneObject(std::string const& _contractName = "") const; + eth::LinkerObject const& cloneObject(std::string const& _contractName) const; /// @returns normal contract assembly items - eth::AssemblyItems const* assemblyItems(std::string const& _contractName = "") const; + eth::AssemblyItems const* assemblyItems(std::string const& _contractName) const; /// @returns runtime contract assembly items - eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName = "") const; + eth::AssemblyItems const* runtimeAssemblyItems(std::string const& _contractName) const; /// @returns the string that provides a mapping between bytecode and sourcecode or a nullptr /// if the contract does not (yet) have bytecode. - std::string const* sourceMapping(std::string const& _contractName = "") const; + std::string const* sourceMapping(std::string const& _contractName) const; /// @returns the string that provides a mapping between runtime bytecode and sourcecode. /// if the contract does not (yet) have bytecode. - std::string const* runtimeSourceMapping(std::string const& _contractName = "") const; + std::string const* runtimeSourceMapping(std::string const& _contractName) const; /// @return a verbose text representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - std::string assemblyString(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + std::string assemblyString(std::string const& _contractName, StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representation of the assembly. /// @arg _sourceCodes is the map of input files to source code strings /// Prerequisite: Successful compilation. - Json::Value assemblyJSON(std::string const& _contractName = "", StringMap _sourceCodes = StringMap()) const; + Json::Value assemblyJSON(std::string const& _contractName, StringMap _sourceCodes = StringMap()) const; /// @returns a JSON representing the contract ABI. /// Prerequisite: Successful call to parse or compile. - Json::Value const& contractABI(std::string const& _contractName = "") const; + Json::Value const& contractABI(std::string const& _contractName) const; /// @returns a JSON representing the contract's user documentation. /// Prerequisite: Successful call to parse or compile. @@ -259,6 +269,9 @@ private: /// Helper function to return path converted strings. std::string sanitizePath(std::string const& _path) const { return boost::filesystem::path(_path).generic_string(); } + /// @returns true if the contract is requested to be compiled. + bool isRequestedContract(ContractDefinition const& _contract) const; + /// Compile a single contract and put the result in @a _compiledContracts. void compileContract( ContractDefinition const& _contract, @@ -266,8 +279,8 @@ private: ); void link(); - Contract const& contract(std::string const& _contractName = "") const; - Source const& source(std::string const& _sourceName = "") const; + Contract const& contract(std::string const& _contractName) const; + Source const& source(std::string const& _sourceName) const; /// @returns the parsed contract with the supplied name. Throws an exception if the contract /// does not exist. @@ -297,6 +310,7 @@ private: ReadCallback::Callback m_smtQuery; bool m_optimize = false; unsigned m_optimizeRuns = 200; + std::set<std::string> m_requestedContractNames; std::map<std::string, h160> m_libraries; /// list of path prefix remappings, e.g. mylibrary: github.com/ethereum = /usr/local/ethereum /// "context:prefix=target" diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index 62d22999..aeafaf2d 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -49,6 +49,21 @@ void SourceReferenceFormatter::printSourceLocation( if (startLine == endLine) { string line = scanner.lineAtPosition(_location->start); + + int locationLength = endColumn - startColumn; + if (locationLength > 150) + { + line = line.substr(0, startColumn + 35) + " ... " + line.substr(endColumn - 35); + endColumn = startColumn + 75; + locationLength = 75; + } + if (line.length() > 150) + { + line = " ... " + line.substr(startColumn, locationLength) + " ... "; + startColumn = 5; + endColumn = startColumn + locationLength; + } + _stream << line << endl; for_each( line.cbegin(), diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index b4fbbef9..430739ac 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -92,6 +92,22 @@ Json::Value formatErrorWithException( return formatError(_warning, _type, _component, message, formattedMessage, location); } +set<string> requestedContractNames(Json::Value const& _outputSelection) +{ + set<string> names; + for (auto const& sourceName: _outputSelection.getMemberNames()) + { + for (auto const& contractName: _outputSelection[sourceName].getMemberNames()) + { + /// Consider the "all sources" shortcuts as requesting everything. + if (contractName == "*" || contractName == "") + return set<string>(); + names.insert((sourceName == "*" ? "" : sourceName) + ":" + contractName); + } + } + return names; +} + /// Returns true iff @a _hash (hex with 0x prefix) is the Keccak256 hash of the binary data in @a _content. bool hashMatchesContent(string const& _hash, string const& _content) { @@ -265,6 +281,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) Json::Value metadataSettings = settings.get("metadata", Json::Value()); m_compilerStack.useMetadataLiteralSources(metadataSettings.get("useLiteralContent", Json::Value(false)).asBool()); + Json::Value outputSelection = settings.get("outputSelection", Json::Value()); + m_compilerStack.setRequestedContractNames(requestedContractNames(outputSelection)); + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compilerStack.scanner(_sourceName); }; try |