diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/PostTypeChecker.cpp | 36 | ||||
-rw-r--r-- | libsolidity/analysis/PostTypeChecker.h | 5 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 62 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 11 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 2 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 6 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 17 | ||||
-rw-r--r-- | libsolidity/codegen/ArrayUtils.cpp | 49 | ||||
-rw-r--r-- | libsolidity/codegen/ArrayUtils.h | 6 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 29 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 6 |
12 files changed, 155 insertions, 78 deletions
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp index fbc72e52..19d0b708 100644 --- a/libsolidity/analysis/PostTypeChecker.cpp +++ b/libsolidity/analysis/PostTypeChecker.cpp @@ -21,6 +21,8 @@ #include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/interface/Version.h> +#include <libdevcore/Algorithms.h> + #include <boost/range/adaptor/map.hpp> #include <memory> @@ -47,7 +49,7 @@ void PostTypeChecker::endVisit(ContractDefinition const&) { solAssert(!m_currentConstVariable, ""); for (auto declaration: m_constVariables) - if (auto identifier = findCycle(declaration)) + if (auto identifier = findCycle(*declaration)) m_errorReporter.typeError( declaration->location(), "The value of the constant " + declaration->name() + @@ -87,20 +89,24 @@ bool PostTypeChecker::visit(Identifier const& _identifier) return true; } -VariableDeclaration const* PostTypeChecker::findCycle( - VariableDeclaration const* _startingFrom, - set<VariableDeclaration const*> const& _seen -) +VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom) { - if (_seen.count(_startingFrom)) - return _startingFrom; - else if (m_constVariableDependencies.count(_startingFrom)) + auto visitor = [&](VariableDeclaration const& _variable, CycleDetector<VariableDeclaration>& _cycleDetector) { - set<VariableDeclaration const*> seen(_seen); - seen.insert(_startingFrom); - for (auto v: m_constVariableDependencies[_startingFrom]) - if (findCycle(v, seen)) - return v; - } - return nullptr; + // Iterating through the dependencies needs to be deterministic and thus cannot + // depend on the memory layout. + // Because of that, we sort by AST node id. + vector<VariableDeclaration const*> dependencies( + m_constVariableDependencies[&_variable].begin(), + m_constVariableDependencies[&_variable].end() + ); + sort(dependencies.begin(), dependencies.end(), [](VariableDeclaration const* _a, VariableDeclaration const* _b) -> bool + { + return _a->id() < _b->id(); + }); + for (auto v: dependencies) + if (_cycleDetector.run(*v)) + return; + }; + return CycleDetector<VariableDeclaration>(visitor).run(_startingFrom); } diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h index bafc1ae6..4f9dac6e 100644 --- a/libsolidity/analysis/PostTypeChecker.h +++ b/libsolidity/analysis/PostTypeChecker.h @@ -55,10 +55,7 @@ private: virtual bool visit(Identifier const& _identifier) override; - VariableDeclaration const* findCycle( - VariableDeclaration const* _startingFrom, - std::set<VariableDeclaration const*> const& _seen = std::set<VariableDeclaration const*>{} - ); + VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom); ErrorReporter& m_errorReporter; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 620dfca4..a252742d 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -320,7 +320,7 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c { auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name())); solAssert(baseContract, ""); - if (!base->arguments().empty()) + if (base->arguments() && !base->arguments()->empty()) argumentsNeeded.erase(baseContract); } } @@ -506,30 +506,46 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) // Interfaces do not have constructors, so there are zero parameters. parameterTypes = ContractType(*base).newExpressionType()->parameterTypes(); - if (!arguments.empty() && parameterTypes.size() != arguments.size()) + if (arguments) { - m_errorReporter.typeError( - _inheritance.location(), - "Wrong argument count for constructor call: " + - toString(arguments.size()) + - " arguments given but expected " + - toString(parameterTypes.size()) + - "." - ); - return; - } + bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); - for (size_t i = 0; i < arguments.size(); ++i) - if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - m_errorReporter.typeError( - arguments[i]->location(), - "Invalid type for argument in constructor call. " - "Invalid implicit conversion from " + - type(*arguments[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested." - ); + if (parameterTypes.size() != arguments->size()) + { + if (arguments->size() == 0 && !v050) + m_errorReporter.warning( + _inheritance.location(), + "Wrong argument count for constructor call: " + + toString(arguments->size()) + + " arguments given but expected " + + toString(parameterTypes.size()) + + "." + ); + else + { + m_errorReporter.typeError( + _inheritance.location(), + "Wrong argument count for constructor call: " + + toString(arguments->size()) + + " arguments given but expected " + + toString(parameterTypes.size()) + + "." + ); + return; + } + } + for (size_t i = 0; i < arguments->size(); ++i) + if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + m_errorReporter.typeError( + (*arguments)[i]->location(), + "Invalid type for argument in constructor call. " + "Invalid implicit conversion from " + + type(*(*arguments)[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested." + ); + } } void TypeChecker::endVisit(UsingForDirective const& _usingFor) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 56bb412c..bc85349b 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -425,19 +425,22 @@ public: InheritanceSpecifier( SourceLocation const& _location, ASTPointer<UserDefinedTypeName> const& _baseName, - std::vector<ASTPointer<Expression>> _arguments + std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments ): - ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} + ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; UserDefinedTypeName const& name() const { return *m_baseName; } - std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } + // Returns nullptr if no argument list was given (``C``). + // If an argument list is given (``C(...)``), the arguments are returned + // as a vector of expressions. Note that this vector can be empty (``C()``). + std::vector<ASTPointer<Expression>> const* arguments() const { return m_arguments.get(); } private: ASTPointer<UserDefinedTypeName> m_baseName; - std::vector<ASTPointer<Expression>> m_arguments; + std::unique_ptr<std::vector<ASTPointer<Expression>>> m_arguments; }; /** diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 4fef67c3..94932eca 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) { setJsonNode(_node, "InheritanceSpecifier", { make_pair("baseName", toJson(_node.name())), - make_pair("arguments", toJson(_node.arguments())) + make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::Value(Json::arrayValue)) }); return false; } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 70ee997e..dac414fc 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -94,7 +94,8 @@ void InheritanceSpecifier::accept(ASTVisitor& _visitor) if (_visitor.visit(*this)) { m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } @@ -104,7 +105,8 @@ void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const if (_visitor.visit(*this)) { m_baseName->accept(_visitor); - listAccept(m_arguments, _visitor); + if (m_arguments) + listAccept(*m_arguments, _visitor); } _visitor.endVisit(*this); } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 42fd1c3d..ac1d3b01 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -28,6 +28,7 @@ #include <libdevcore/CommonData.h> #include <libdevcore/SHA3.h> #include <libdevcore/UTF8.h> +#include <libdevcore/Algorithms.h> #include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/replace.hpp> @@ -1971,25 +1972,19 @@ bool StructType::recursive() const { if (!m_recursive.is_initialized()) { - set<StructDefinition const*> structsSeen; - function<bool(StructType const*)> check = [&](StructType const* t) -> bool + auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector) { - StructDefinition const* str = &t->structDefinition(); - if (structsSeen.count(str)) - return true; - structsSeen.insert(str); - for (ASTPointer<VariableDeclaration> const& variable: str->members()) + for (ASTPointer<VariableDeclaration> const& variable: _struct.members()) { Type const* memberType = variable->annotation().type.get(); while (dynamic_cast<ArrayType const*>(memberType)) memberType = dynamic_cast<ArrayType const*>(memberType)->baseType().get(); if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType)) - if (check(innerStruct)) - return true; + if (_cycleDetector.run(innerStruct->structDefinition())) + return; } - return false; }; - m_recursive = check(this); + m_recursive = (CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr); } return *m_recursive; } diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 4703fc1f..0fe66d2d 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -774,6 +774,55 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const ); } +void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const +{ + solAssert(_type.location() == DataLocation::Storage, ""); + solAssert(_type.isDynamicallySized(), ""); + if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32) + solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type."); + + if (_type.isByteArray()) + { + // We almost always just add 2 (length of byte arrays is shifted left by one) + // except for the case where we transition from a short byte array + // to a long byte array, there we have to copy. + // This happens if the length is exactly 31, which means that the + // lowest-order byte (we actually use a mask with fewer bits) must + // be (31*2+0) = 62 + + m_context.appendInlineAssembly(R"({ + let data := sload(ref) + let shifted_length := and(data, 63) + // We have to copy if length is exactly 31, because that marks + // the transition between in-place and out-of-place storage. + switch shifted_length + case 62 + { + mstore(0, ref) + let data_area := keccak256(0, 0x20) + sstore(data_area, and(data, not(0xff))) + // New length is 32, encoded as (32 * 2 + 1) + sstore(ref, 65) + // Replace ref variable by new length + ref := 32 + } + default + { + sstore(ref, add(data, 2)) + // Replace ref variable by new length + if iszero(and(data, 1)) { data := shifted_length } + ref := add(div(data, 2), 1) + } + })", {"ref"}); + } + else + m_context.appendInlineAssembly(R"({ + let new_length := add(sload(ref), 1) + sstore(ref, new_length) + ref := new_length + })", {"ref"}); +} + void ArrayUtils::clearStorageLoop(TypePointer const& _type) const { m_context.callLowLevelFunction( diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h index f3ddc4ee..99786397 100644 --- a/libsolidity/codegen/ArrayUtils.h +++ b/libsolidity/codegen/ArrayUtils.h @@ -67,6 +67,12 @@ public: /// Stack pre: reference (excludes byte offset) new_length /// Stack post: void resizeDynamicArray(ArrayType const& _type) const; + /// Increments the size of a dynamic array by one. + /// Does not touch the new data element. In case of a byte array, this might move the + /// data. + /// Stack pre: reference (excludes byte offset) + /// Stack post: new_length + void incrementDynamicArraySize(ArrayType const& _type) const; /// Appends a loop that clears a sequence of storage slots of the given type (excluding end). /// Stack pre: end_ref start_ref /// Stack post: end_ref diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index ebd9139a..d3a7e4ea 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -157,8 +157,8 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c ); solAssert(baseContract, ""); - if (!m_baseArguments.count(baseContract->constructor()) && !base->arguments().empty()) - m_baseArguments[baseContract->constructor()] = &base->arguments(); + if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty()) + m_baseArguments[baseContract->constructor()] = base->arguments(); } } // Initialization of state variables in base-to-derived order. diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 76aa6843..57d49ac6 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -821,24 +821,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) function.kind() == FunctionType::Kind::ArrayPush ? make_shared<ArrayType>(DataLocation::Storage, paramType) : make_shared<ArrayType>(DataLocation::Storage); - // get the current length - ArrayUtils(m_context).retrieveLength(*arrayType); - m_context << Instruction::DUP1; - // stack: ArrayReference currentLength currentLength - m_context << u256(1) << Instruction::ADD; - // stack: ArrayReference currentLength newLength - m_context << Instruction::DUP3 << Instruction::DUP2; - ArrayUtils(m_context).resizeDynamicArray(*arrayType); - m_context << Instruction::SWAP2 << Instruction::SWAP1; - // stack: newLength ArrayReference oldLength - ArrayUtils(m_context).accessIndex(*arrayType, false); - // stack: newLength storageSlot slotOffset + // stack: ArrayReference arguments[0]->accept(*this); + TypePointer const& argType = arguments[0]->annotation().type; + // stack: ArrayReference argValue + utils().moveToStackTop(argType->sizeOnStack(), 1); + // stack: argValue ArrayReference + m_context << Instruction::DUP1; + ArrayUtils(m_context).incrementDynamicArraySize(*arrayType); + // stack: argValue ArrayReference newLength + m_context << Instruction::SWAP1; + // stack: argValue newLength ArrayReference + m_context << u256(1) << Instruction::DUP3 << Instruction::SUB; + // stack: argValue newLength ArrayReference (newLength-1) + ArrayUtils(m_context).accessIndex(*arrayType, false); + // stack: argValue newLength storageSlot slotOffset + utils().moveToStackTop(3, argType->sizeOnStack()); // stack: newLength storageSlot slotOffset argValue TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType()); solAssert(type, ""); - utils().convertType(*arguments[0]->annotation().type, *type); + utils().convertType(*argType, *type); utils().moveToStackTop(1 + type->sizeOnStack()); utils().moveToStackTop(1 + type->sizeOnStack()); // stack: newLength argValue storageSlot slotOffset diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 3dbd4c8f..9a7731d8 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -286,17 +286,17 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName()); - vector<ASTPointer<Expression>> arguments; + unique_ptr<vector<ASTPointer<Expression>>> arguments; if (m_scanner->currentToken() == Token::LParen) { m_scanner->next(); - arguments = parseFunctionCallListArguments(); + arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments())); nodeFactory.markEndPosition(); expectToken(Token::RParen); } else nodeFactory.setEndPositionFromNode(name); - return nodeFactory.createNode<InheritanceSpecifier>(name, arguments); + return nodeFactory.createNode<InheritanceSpecifier>(name, std::move(arguments)); } Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token) |