diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/GlobalContext.cpp | 2 | ||||
-rw-r--r-- | libsolidity/analysis/SemVerHandler.h | 6 | ||||
-rw-r--r-- | libsolidity/analysis/SyntaxChecker.cpp | 19 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 34 | ||||
-rw-r--r-- | libsolidity/ast/AST.cpp | 6 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 66 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 11 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 3 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 50 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.h | 2 | ||||
-rw-r--r-- | libsolidity/formal/Why3Translator.cpp | 14 | ||||
-rw-r--r-- | libsolidity/formal/Why3Translator.h | 1 | ||||
-rw-r--r-- | libsolidity/grammar.txt | 4 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmCodeGen.cpp | 26 | ||||
-rw-r--r-- | libsolidity/inlineasm/AsmParser.cpp | 14 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 20 | ||||
-rw-r--r-- | libsolidity/interface/GasEstimator.cpp | 2 |
19 files changed, 230 insertions, 54 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index a7ffcfad..d075949e 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -48,6 +48,8 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared< make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::MulMod)), make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)), + make_shared<MagicVariableDeclaration>("keccak256", + make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)), make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)), make_shared<MagicVariableDeclaration>("log1", diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h index 3c110b19..e3b642db 100644 --- a/libsolidity/analysis/SemVerHandler.h +++ b/libsolidity/analysis/SemVerHandler.h @@ -40,6 +40,12 @@ struct SemVerVersion std::string prerelease; std::string build; + unsigned major() const { return numbers[0]; } + unsigned minor() const { return numbers[1]; } + unsigned patch() const { return numbers[2]; } + + bool isPrerelease() const { return !prerelease.empty(); } + explicit SemVerVersion(std::string const& _versionString = "0.0.0"); }; diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index a95b4879..dbaa15ed 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -52,13 +52,22 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit) { if (!m_versionPragmaFound) { + string errorString("Source file does not specify required compiler version!"); + SemVerVersion recommendedVersion{string(VersionString)}; + if (!recommendedVersion.isPrerelease()) + errorString += + "Consider adding \"pragma solidity ^" + + to_string(recommendedVersion.major()) + + string(".") + + to_string(recommendedVersion.minor()) + + string(".") + + to_string(recommendedVersion.patch()); + string(";\""); + auto err = make_shared<Error>(Error::Type::Warning); *err << errinfo_sourceLocation(_sourceUnit.location()) << - errinfo_comment( - string("Source file does not specify required compiler version! ") + - string("Consider adding \"pragma solidity ^") + VersionNumber + string(";\".") - ); + errinfo_comment(errorString); m_errors.push_back(err); } } @@ -67,7 +76,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) { solAssert(!_pragma.tokens().empty(), ""); solAssert(_pragma.tokens().size() == _pragma.literals().size(), ""); - if (_pragma.tokens()[0] != Token::Identifier && _pragma.literals()[0] != "solidity") + if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity") syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\""); else { diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index ae7c13c8..46f4f7f6 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -609,6 +609,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return false; pushes = 1; } + else + return false; for (unsigned i = 0; i < pushes; ++i) _assembly.append(u256(0)); // just to verify the stack height } @@ -716,11 +718,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (ref->dataStoredIn(DataLocation::Storage)) { - auto err = make_shared<Error>(Error::Type::Warning); - *err << - errinfo_sourceLocation(varDecl.location()) << - errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?"); - m_errors.push_back(err); + warning( + varDecl.location(), + "Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?" + ); } } varDecl.accept(*this); @@ -879,6 +880,10 @@ bool TypeChecker::visit(Conditional const& _conditional) TypePointer trueType = type(_conditional.trueExpression())->mobileType(); TypePointer falseType = type(_conditional.falseExpression())->mobileType(); + if (!trueType) + fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type."); + if (!falseType) + fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type."); TypePointer commonType = Type::commonType(trueType, falseType); if (!commonType) @@ -985,10 +990,16 @@ bool TypeChecker::visit(TupleExpression const& _tuple) types.push_back(type(*components[i])); if (_tuple.isInlineArray()) solAssert(!!types[i], "Inline array cannot have empty components"); - if (i == 0 && _tuple.isInlineArray()) - inlineArrayType = types[i]->mobileType(); - else if (_tuple.isInlineArray() && inlineArrayType) - inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType()); + if (_tuple.isInlineArray()) + { + if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) + fatalTypeError(components[i]->location(), "Invalid mobile type."); + + if (i == 0) + inlineArrayType = types[i]->mobileType(); + else if (inlineArrayType) + inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType()); + } } else types.push_back(TypePointer()); @@ -1375,6 +1386,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) } else if (exprType->category() == Type::Category::FixedBytes) annotation.isLValue = false; + else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType.get())) + { + if (ContractType const* contractType = dynamic_cast<decltype(contractType)>(typeType->actualType().get())) + annotation.isLValue = annotation.referencedDeclaration->isLValue(); + } return false; } diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 294daa13..695d9881 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -151,7 +151,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter if (signaturesSeen.count(functionSignature) == 0) { signaturesSeen.insert(functionSignature); - FixedHash<4> hash(dev::sha3(functionSignature)); + FixedHash<4> hash(dev::keccak256(functionSignature)); m_interfaceFunctionList->push_back(make_pair(hash, fun)); } } @@ -189,6 +189,7 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const m_inheritableMembers.reset(new vector<Declaration const*>()); auto addInheritableMember = [&](Declaration const* _decl) { + solAssert(_decl, "addInheritableMember got a nullpointer."); if (memberSeen.count(_decl->name()) == 0 && _decl->isVisibleInDerivedContracts()) { memberSeen.insert(_decl->name()); @@ -204,6 +205,9 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const for (StructDefinition const* s: definedStructs()) addInheritableMember(s); + + for (EnumDefinition const* e: definedEnums()) + addInheritableMember(e); } return *m_inheritableMembers; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 8fd1584d..7ed4ddce 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -623,7 +623,7 @@ public: virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } - bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); } + bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); } /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; /// @returns true if this variable is a parameter (not return parameter) of an external function. diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 4b5f12ce..7cfed3c8 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -23,6 +23,7 @@ #include <libsolidity/ast/Types.h> #include <limits> #include <boost/range/adaptor/reversed.hpp> +#include <boost/range/adaptor/sliced.hpp> #include <libdevcore/CommonIO.h> #include <libdevcore/CommonData.h> #include <libdevcore/SHA3.h> @@ -197,7 +198,9 @@ TypePointer Type::forLiteral(Literal const& _literal) TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) { - if (_b->isImplicitlyConvertibleTo(*_a)) + if (!_a || !_b) + return TypePointer(); + else if (_b->isImplicitlyConvertibleTo(*_a)) return _a; else if (_a->isImplicitlyConvertibleTo(*_b)) return _b; @@ -241,7 +244,8 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition seenFunctions.insert(function); FunctionType funType(*function, false); if (auto fun = funType.asMemberFunction(true, true)) - members.push_back(MemberList::Member(function->name(), fun, function)); + if (_type.isImplicitlyConvertibleTo(*fun->selfType())) + members.push_back(MemberList::Member(function->name(), fun, function)); } } return members; @@ -481,7 +485,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal !all_of(radixPoint + 1, _literal.value().end(), ::isdigit) || !all_of(_literal.value().begin(), radixPoint, ::isdigit) ) - throw; + return make_tuple(false, rational(0)); //Only decimal notation allowed here, leading zeros would switch to octal. auto fractionalBegin = find_if_not( radixPoint + 1, @@ -574,7 +578,11 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const { FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo); if (!isFractional()) - return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + { + if (integerType()) + return fixedBytes.numBytes() * 8 >= integerType()->numBits(); + return false; + } else return false; } @@ -700,6 +708,34 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ value = rational(denominator, numerator); break; } + case Token::SHL: + { + using boost::multiprecision::pow; + if (fractional) + return TypePointer(); + else if (other.m_value < 0) + return TypePointer(); + else if (other.m_value > numeric_limits<uint32_t>::max()) + return TypePointer(); + uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); + value = m_value.numerator() * pow(bigint(2), exponent); + break; + } + // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue + // determines the resulting type and the type of shift (SAR or SHR). + case Token::SAR: + { + using boost::multiprecision::pow; + if (fractional) + return TypePointer(); + else if (other.m_value < 0) + return TypePointer(); + else if (other.m_value > numeric_limits<uint32_t>::max()) + return TypePointer(); + uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>(); + value = rational(m_value.numerator() / pow(bigint(2), exponent), 1); + break; + } default: return TypePointer(); } @@ -1291,7 +1327,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con if (m_super) { // add the most derived of all functions which are visible in derived contracts - for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) + auto bases = m_contract.annotation().linearizedBaseContracts; + solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract."); + // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`. + for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) for (FunctionDefinition const* function: base->definedFunctions()) { if (!function->isVisibleInDerivedContracts()) @@ -1624,7 +1663,17 @@ TypePointer TupleType::mobileType() const { TypePointers mobiles; for (auto const& c: components()) - mobiles.push_back(c ? c->mobileType() : TypePointer()); + { + if (c) + { + auto mt = c->mobileType(); + if (!mt) + return TypePointer(); + mobiles.push_back(mt); + } + else + mobiles.push_back(TypePointer()); + } return make_shared<TupleType>(mobiles); } @@ -2033,7 +2082,7 @@ string FunctionType::externalSignature() const u256 FunctionType::externalIdentifier() const { - return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature()))); + return FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(externalSignature()))); } TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) @@ -2065,6 +2114,9 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const { + if (_bound && m_parameterTypes.empty()) + return FunctionTypePointer(); + TypePointers parameterTypes; for (auto const& t: m_parameterTypes) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 9173f39a..3f94d11a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -623,6 +623,7 @@ public: } virtual unsigned storageBytes() const override { return 20; } virtual bool canLiveOutsideStorage() const override { return true; } + virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; } virtual bool isValueType() const override { return true; } virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; @@ -871,7 +872,12 @@ public: m_isConstant(_isConstant), m_isPayable(_isPayable), m_declaration(_declaration) - {} + { + solAssert( + !m_bound || !m_parameterTypes.empty(), + "Attempted construction of bound function without self type" + ); + } TypePointers parameterTypes() const; std::vector<std::string> parameterNames() const; @@ -939,8 +945,9 @@ public: /// removed and the location of reference types is changed from CallData to Memory. /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. + /// Returns empty shared pointer on a failure. Namely, if a bound function has no parameters. /// @param _inLibrary if true, uses DelegateCall as location. - /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`. + /// @param _bound if true, the arguments are placed as `arg1.functionName(arg2, ..., argn)`. FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; private: diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index ec496df8..e064c1a6 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -368,8 +368,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << Instruction::MUL; } else if (targetTypeCategory == Type::Category::Enum) + { + solAssert(_typeOnStack.mobileType(), ""); // just clean convertType(_typeOnStack, *_typeOnStack.mobileType(), true); + } else if (targetTypeCategory == Type::Category::FixedPoint) { solAssert( diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 18b42fce..ebb84784 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -575,7 +575,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) return true; } ); - solAssert(errors.empty(), "Code generation for inline assembly with errors requested."); + solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested."); m_context.setStackOffset(startStackHeight); return false; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 26acd8a4..9a096e2d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -56,8 +56,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage)) { // reference type, only convert value to mobile type and do final conversion in storeValue. - utils().convertType(*type, *type->mobileType()); - type = type->mobileType(); + auto mt = type->mobileType(); + solAssert(mt, ""); + utils().convertType(*type, *mt); + type = mt; } else { @@ -670,7 +672,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } if (!event.isAnonymous()) { - m_context << u256(h256::Arith(dev::sha3(function.externalSignature()))); + m_context << u256(h256::Arith(dev::keccak256(function.externalSignature()))); ++numIndexed; } solAssert(numIndexed <= 4, "Too many indexed arguments."); @@ -861,11 +863,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) } // Special processing for TypeType because we do not want to visit the library itself - // for internal functions. + // for internal functions, or enum/struct definitions. if (TypeType const* type = dynamic_cast<TypeType const*>(_memberAccess.expression().annotation().type.get())) { if (dynamic_cast<ContractType const*>(type->actualType().get())) { + solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get())) { if (funType->location() != FunctionType::Location::Internal) @@ -883,6 +886,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) m_context << m_context.functionEntryLabel(*function).pushTag(); } } + else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get())) + { + // no-op + } + else if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) + appendVariable(*variable, static_cast<Expression const&>(_memberAccess)); else _memberAccess.expression().accept(*this); } @@ -1196,15 +1205,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag(); else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration)) - { - if (!variable->isConstant()) - setLValueFromDeclaration(*declaration, _identifier); - else - { - variable->value()->accept(*this); - utils().convertType(*variable->value()->annotation().type, *variable->annotation().type); - } - } + appendVariable(*variable, static_cast<Expression const&>(_identifier)); else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) { if (contract->isLibrary()) @@ -1432,11 +1433,17 @@ void ExpressionCompiler::appendExternalFunctionCall( // Evaluate arguments. TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); - bool manualFunctionId = + bool manualFunctionId = false; + if ( (funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) && - !_arguments.empty() && - _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == + !_arguments.empty() + ) + { + solAssert(_arguments.front()->annotation().type->mobileType(), ""); + manualFunctionId = + _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == CompilerUtils::dataStartOffset; + } if (manualFunctionId) { // If we have a Bare* and the first type has exactly 4 bytes, use it as @@ -1633,6 +1640,17 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, utils().storeInMemoryDynamic(_expectedType); } +void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression) +{ + if (!_variable.isConstant()) + setLValueFromDeclaration(_variable, _expression); + else + { + _variable.value()->accept(*this); + utils().convertType(*_variable.value()->annotation().type, *_variable.annotation().type); + } +} + void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) { if (m_context.isLocalVariable(&_declaration)) diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h index 43a92a10..f4ce1fec 100644 --- a/libsolidity/codegen/ExpressionCompiler.h +++ b/libsolidity/codegen/ExpressionCompiler.h @@ -103,6 +103,8 @@ private: /// expected to be on the stack and is updated by this call. void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression); + /// Appends code for a variable that might be a constant or not + void appendVariable(VariableDeclaration const& _variable, Expression const& _expression); /// Sets the current LValue to a new one (of the appropriate type) from the given declaration. /// Also retrieves the value if it was not requested by @a _expression. void setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression); diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp index f3831b40..813fa3ab 100644 --- a/libsolidity/formal/Why3Translator.cpp +++ b/libsolidity/formal/Why3Translator.cpp @@ -757,6 +757,20 @@ bool Why3Translator::visit(Literal const& _literal) return false; } +bool Why3Translator::visit(PragmaDirective const& _pragma) +{ + if (_pragma.tokens().empty()) + error(_pragma, "Not supported"); + else if (_pragma.literals().empty()) + error(_pragma, "Not supported"); + else if (_pragma.literals()[0] != "solidity") + error(_pragma, "Not supported"); + else if (_pragma.tokens()[0] != Token::Identifier) + error(_pragma, "A literal 'solidity' is not an identifier. Strange"); + + return false; +} + bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const { return contains(m_currentContract.stateVariables, _var); diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h index 22bfff89..4fdac385 100644 --- a/libsolidity/formal/Why3Translator.h +++ b/libsolidity/formal/Why3Translator.h @@ -94,6 +94,7 @@ private: virtual bool visit(IndexAccess const& _node) override; virtual bool visit(Identifier const& _node) override; virtual bool visit(Literal const& _node) override; + virtual bool visit(PragmaDirective const& _node) override; virtual bool visitNode(ASTNode const& _node) override { diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 755cf281..d84ee10c 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -77,7 +77,7 @@ Expression = | Expression? (',' Expression) | PrimaryExpression -PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | StringLiteral +PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | HexLiteral | StringLiteral FunctionCall = ( PrimaryExpression | NewExpression | TypeName ) ( ( '.' Identifier ) | ( '[' Expression ']' ) )* '(' Expression? ( ',' Expression )* ')' NewExpression = 'new' Identifier @@ -88,8 +88,8 @@ BooleanLiteral = 'true' | 'false' NumberLiteral = '0x'? [0-9]+ (' ' NumberUnit)? NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' +HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' - Identifier = [a-zA-Z_] [a-zA-Z_0-9]* ElementaryTypeName = 'address' | 'bool' | 'string' | 'var' diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp index 53d19b0a..5d920cb7 100644 --- a/libsolidity/inlineasm/AsmCodeGen.cpp +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -23,6 +23,7 @@ #include <libsolidity/inlineasm/AsmCodeGen.h> #include <memory> #include <functional> +#include <libdevcore/CommonIO.h> #include <libevmasm/Assembly.h> #include <libevmasm/SourceLocation.h> #include <libevmasm/Instruction.h> @@ -213,10 +214,31 @@ public: void operator()(assembly::Block const& _block) { size_t numVariables = m_state.variables.size(); + int deposit = m_state.assembly.deposit(); std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); - // pop variables - // we deliberately do not check stack height + deposit = m_state.assembly.deposit() - deposit; + m_state.assembly.setSourceLocation(_block.location); + + // issue warnings for stack height discrepancies + if (deposit < 0) + { + m_state.addError( + Error::Type::Warning, + "Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.", + _block.location + ); + } + else if (deposit > 0) + { + m_state.addError( + Error::Type::Warning, + "Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.", + _block.location + ); + } + + // pop variables while (m_state.variables.size() > numVariables) { m_state.assembly.append(solidity::Instruction::POP); diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 5c7163ee..8d2c2ed4 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -95,7 +95,9 @@ assembly::Statement Parser::parseStatement() fatalParserError("Label name / variable name must precede \":\"."); assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement); m_scanner->next(); - if (m_scanner->currentToken() == Token::Assign) + // identifier:=: should be parsed as identifier: =: (i.e. a label), + // while identifier:= (being followed by a non-colon) as identifier := (assignment). + if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon) { // functional assignment FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location); @@ -133,6 +135,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) // Allowed instructions, lowercase names. static map<string, dev::solidity::Instruction> s_instructions; if (s_instructions.empty()) + { for (auto const& instruction: solidity::c_instructions) { if ( @@ -141,24 +144,29 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher) ) continue; string name = instruction.first; - if (instruction.second == solidity::Instruction::SUICIDE) - name = "selfdestruct"; transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); s_instructions[name] = instruction.second; } + // add alias for selfdestruct + s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE; + } + Statement ret; switch (m_scanner->currentToken()) { case Token::Identifier: case Token::Return: case Token::Byte: + case Token::Address: { string literal; if (m_scanner->currentToken() == Token::Return) literal = "return"; else if (m_scanner->currentToken() == Token::Byte) literal = "byte"; + else if (m_scanner->currentToken() == Token::Address) + literal = "address"; else literal = m_scanner->currentLiteral(); // first search the set of instructions. diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index ec6b5d2e..efbbd237 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -21,8 +21,10 @@ * Full-stack compiler that converts a source code string to bytecode. */ -#include <boost/algorithm/string.hpp> -#include <boost/filesystem.hpp> +#include <libsolidity/interface/CompilerStack.h> + +#include <libsolidity/interface/Version.h> +#include <libsolidity/analysis/SemVerHandler.h> #include <libsolidity/ast/AST.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> @@ -32,12 +34,15 @@ #include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/analysis/SyntaxChecker.h> #include <libsolidity/codegen/Compiler.h> -#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/InterfaceHandler.h> #include <libsolidity/formal/Why3Translator.h> #include <libdevcore/SHA3.h> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> + + using namespace std; using namespace dev; using namespace dev::solidity; @@ -100,6 +105,13 @@ bool CompilerStack::parse() m_errors.clear(); m_parseSuccessful = false; + if (SemVerVersion{string(VersionString)}.isPrerelease()) + { + auto err = make_shared<Error>(Error::Type::Warning); + *err << errinfo_comment("This is a pre-release compiler version, please do not use it in production."); + m_errors.push_back(err); + } + vector<string> sourcesToParse; for (auto const& s: m_sources) sourcesToParse.push_back(s.first); @@ -302,7 +314,7 @@ dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const if (obj.bytecode.empty() || !obj.linkReferences.empty()) return dev::h256(); else - return dev::sha3(obj.bytecode); + return dev::keccak256(obj.bytecode); } Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp index 99ed75bc..1c804b78 100644 --- a/libsolidity/interface/GasEstimator.cpp +++ b/libsolidity/interface/GasEstimator.cpp @@ -136,7 +136,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation( ExpressionClasses& classes = state->expressionClasses(); using Id = ExpressionClasses::Id; using Ids = vector<Id>; - Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature))))); + Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(_signature))))); Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))}); classes.forceEqual(hashValue, Instruction::DIV, Ids{ calldata, |