diff options
-rw-r--r-- | AST.cpp | 15 | ||||
-rw-r--r-- | AST.h | 31 | ||||
-rw-r--r-- | ArrayUtils.cpp | 1 | ||||
-rw-r--r-- | Compiler.cpp | 7 | ||||
-rw-r--r-- | CompilerUtils.cpp | 4 | ||||
-rw-r--r-- | ExpressionCompiler.cpp | 15 | ||||
-rw-r--r-- | LValue.cpp | 1 | ||||
-rw-r--r-- | Parser.cpp | 10 | ||||
-rw-r--r-- | Parser.h | 3 | ||||
-rw-r--r-- | Types.h | 2 |
10 files changed, 67 insertions, 22 deletions
@@ -189,7 +189,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn for (ContractDefinition const* contract: getLinearizedBaseContracts()) { for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) - if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0) + if (functionsSeen.count(f->getName()) == 0 && f->isPartOfExternalInterface()) { functionsSeen.insert(f->getName()); FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); @@ -197,7 +197,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn } for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) - if (v->isPublic() && functionsSeen.count(v->getName()) == 0) + if (functionsSeen.count(v->getName()) == 0 && v->isPartOfExternalInterface()) { FunctionType ftype(*v); functionsSeen.insert(v->getName()); @@ -322,8 +322,8 @@ string FunctionDefinition::getCanonicalSignature() const bool VariableDeclaration::isLValue() const { - // External function parameters are Read-Only - return !isExternalFunctionParameter(); + // External function parameters and constant declared variables are Read-Only + return !isExternalFunctionParameter() && !m_isConstant; } void VariableDeclaration::checkTypeRequirements() @@ -332,6 +332,13 @@ void VariableDeclaration::checkTypeRequirements() // sets the type. // Note that assignments before the first declaration are legal because of the special scoping // rules inherited from JavaScript. + if (m_isConstant) + { + if (!dynamic_cast<ContractDefinition const*>(getScope())) + BOOST_THROW_EXCEPTION(createTypeError("Illegal use of \"constant\" specifier.")); + if ((m_type && !m_type->isValueType()) || !m_value) + BOOST_THROW_EXCEPTION(createTypeError("Unitialized \"constant\" variable.")); + } if (!m_value) return; if (m_type) @@ -156,6 +156,7 @@ public: /// contract types. virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0; virtual bool isLValue() const { return false; } + virtual bool isPartOfExternalInterface() const { return false; }; protected: virtual Visibility getDefaultVisibility() const { return Visibility::Public; } @@ -415,6 +416,7 @@ public: getVisibility() >= Visibility::Internal; } virtual TypePointer getType(ContractDefinition const*) const override; + virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !getName().empty(); } /// Checks that all parameters have allowed types and calls checkTypeRequirements on the body. void checkTypeRequirements(); @@ -440,13 +442,23 @@ private: class VariableDeclaration: public Declaration { public: - VariableDeclaration(SourceLocation const& _location, ASTPointer<TypeName> const& _type, - ASTPointer<ASTString> const& _name, ASTPointer<Expression> _value, - Visibility _visibility, - bool _isStateVar = false, bool _isIndexed = false): - Declaration(_location, _name, _visibility), - m_typeName(_type), m_value(_value), - m_isStateVariable(_isStateVar), m_isIndexed(_isIndexed) {} + VariableDeclaration( + SourceLocation const& _location, + ASTPointer<TypeName> const& _type, + ASTPointer<ASTString> const& _name, + ASTPointer<Expression> _value, + Visibility _visibility, + bool _isStateVar = false, + bool _isIndexed = false, + bool _isConstant = false + ): + Declaration(_location, _name, _visibility), + m_typeName(_type), + m_value(_value), + m_isStateVariable(_isStateVar), + m_isIndexed(_isIndexed), + m_isConstant(_isConstant){} + virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; @@ -459,21 +471,24 @@ public: void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } virtual bool isLValue() const override; + virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstant; } void checkTypeRequirements(); bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); } bool isExternalFunctionParameter() const; bool isStateVariable() const { return m_isStateVariable; } bool isIndexed() const { return m_isIndexed; } + bool isConstant() const { return m_isConstant; } protected: Visibility getDefaultVisibility() const override { return Visibility::Internal; } private: ASTPointer<TypeName> m_typeName; ///< can be empty ("var") - ASTPointer<Expression> m_value; ///< the assigned value, can be missing + ASTPointer<Expression> m_value; ///< the assigned value, can be missing bool m_isStateVariable; ///< Whether or not this is a contract state variable bool m_isIndexed; ///< Whether this is an indexed variable (used by events). + bool m_isConstant; ///< Whether the variable is a compile-time constant. std::shared_ptr<Type const> m_type; ///< derived type, initially empty }; diff --git a/ArrayUtils.cpp b/ArrayUtils.cpp index f0d7d6a8..a064b2f5 100644 --- a/ArrayUtils.cpp +++ b/ArrayUtils.cpp @@ -125,6 +125,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons CompilerUtils(m_context).loadFromMemoryDynamic(*sourceBaseType, true, true, false); else solAssert(false, "Copying of unknown type requested: " + sourceBaseType->toString()); + solAssert(2 + sourceBaseType->getSizeOnStack() <= 16, "Stack too deep."); m_context << eth::dupInstruction(2 + sourceBaseType->getSizeOnStack()); StorageItem(m_context, *targetBaseType).storeValue(*sourceBaseType, SourceLocation(), true); } diff --git a/Compiler.cpp b/Compiler.cpp index dc6e2c5a..0f4e89de 100644 --- a/Compiler.cpp +++ b/Compiler.cpp @@ -228,6 +228,7 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool { // Retrieve data start offset by adding length to start offset of previous dynamic type unsigned stackDepth = m_context.getStackHeight() - stackHeightOfPreviousDynamicArgument; + solAssert(stackDepth <= 16, "Stack too deep."); m_context << eth::dupInstruction(stackDepth) << eth::dupInstruction(stackDepth); ArrayUtils(m_context).convertLengthToSize(*previousDynamicType, true); m_context << eth::Instruction::ADD; @@ -275,13 +276,14 @@ void Compiler::registerStateVariables(ContractDefinition const& _contract) { for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.getLinearizedBaseContracts())) for (ASTPointer<VariableDeclaration> const& variable: contract->getStateVariables()) - m_context.addStateVariable(*variable); + if (!variable->isConstant()) + m_context.addStateVariable(*variable); } void Compiler::initializeStateVariables(ContractDefinition const& _contract) { for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) - if (variable->getValue()) + if (variable->getValue() && !variable->isConstant()) ExpressionCompiler(m_context, m_optimize).appendStateVariableInitialization(*variable); } @@ -359,6 +361,7 @@ bool Compiler::visit(FunctionDefinition const& _function) stackLayout.push_back(i); stackLayout += vector<int>(c_localVariablesSize, -1); + solAssert(stackLayout.size() <= 17, "Stack too deep."); while (stackLayout.back() != int(stackLayout.size() - 1)) if (stackLayout.back() < 0) { diff --git a/CompilerUtils.cpp b/CompilerUtils.cpp index 7b078e03..e517e384 100644 --- a/CompilerUtils.cpp +++ b/CompilerUtils.cpp @@ -138,6 +138,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) { unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.getBaseStackOffsetOfVariable(_variable)); unsigned const size = _variable.getType()->getSizeOnStack(); + solAssert(stackPosition >= size, "Variable size and position mismatch."); // move variable starting from its top end in the stack if (stackPosition - size + 1 > 16) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_sourceLocation(_variable.getLocation()) @@ -148,8 +149,7 @@ void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable) void CompilerUtils::copyToStackTop(unsigned _stackDepth, unsigned _itemSize) { - if (_stackDepth > 16) - BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Stack too deep.")); + solAssert(_stackDepth <= 16, "Stack too deep."); for (unsigned i = 0; i < _itemSize; ++i) m_context << eth::dupInstruction(_stackDepth); } diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index a9cacc65..92fd7043 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -233,9 +233,12 @@ bool ExpressionCompiler::visit(Assignment const& _assignment) m_currentLValue->retrieveValue(_assignment.getLocation(), true); appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType()); if (lvalueSize > 0) + { + solAssert(itemSize + lvalueSize <= 16, "Stack too deep."); // value [lvalue_ref] updated_value for (unsigned i = 0; i < itemSize; ++i) m_context << eth::swapInstruction(itemSize + lvalueSize) << eth::Instruction::POP; + } } m_currentLValue->storeValue(*_assignment.getRightHandSide().getType(), _assignment.getLocation()); m_currentLValue.reset(); @@ -557,10 +560,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case Location::SHA256: case Location::RIPEMD160: { + _functionCall.getExpression().accept(*this); static const map<Location, u256> contractAddresses{{Location::ECRecover, 1}, {Location::SHA256, 2}, {Location::RIPEMD160, 3}}; m_context << contractAddresses.find(function.getLocation())->second; + for (unsigned i = function.getSizeOnStack(); i > 0; --i) + m_context << eth::swapInstruction(i); appendExternalFunctionCall(function, arguments, true); break; } @@ -852,8 +858,13 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) } else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); - else if (dynamic_cast<VariableDeclaration const*>(declaration)) - setLValueFromDeclaration(*declaration, _identifier); + else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration)) + { + if (!variable->isConstant()) + setLValueFromDeclaration(*declaration, _identifier); + else + variable->getValue()->accept(*this); + } else if (dynamic_cast<ContractDefinition const*>(declaration)) { // no-op @@ -167,6 +167,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc // stack: source_ref target_ref member_offset source_member_ref StorageItem(m_context, *memberType).retrieveValue(_location, true); // stack: source_ref target_ref member_offset source_value... + solAssert(2 + memberType->getSizeOnStack() <= 16, "Stack too deep."); m_context << eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::dupInstruction(2 + memberType->getSizeOnStack()) << eth::Instruction::ADD; // stack: source_ref target_ref member_offset source_value... target_member_ref @@ -317,6 +317,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( nodeFactory.setEndPositionFromNode(type); } bool isIndexed = false; + bool isDeclaredConst = false; ASTPointer<ASTString> identifier; Token::Value token = m_scanner->getCurrentToken(); Declaration::Visibility visibility(Declaration::Visibility::Default); @@ -327,7 +328,13 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( isIndexed = true; m_scanner->next(); } + if (token == Token::Const) + { + isDeclaredConst = true; + m_scanner->next(); + } nodeFactory.markEndPosition(); + if (_options.allowEmptyName && m_scanner->getCurrentToken() != Token::Identifier) { identifier = make_shared<ASTString>(""); @@ -348,7 +355,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration( } return nodeFactory.createNode<VariableDeclaration>(type, identifier, value, visibility, _options.isStateVariable, - isIndexed); + isIndexed, isDeclaredConst); } ASTPointer<ModifierDefinition> Parser::parseModifierDefinition() @@ -913,6 +920,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const // In all other cases, we have an expression statement. Token::Value token(m_scanner->getCurrentToken()); bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier); + if (token == Token::Mapping || token == Token::Var || (mightBeTypeName && m_scanner->peekNextToken() == Token::Identifier)) return LookAheadInfo::VariableDeclarationStatement; @@ -66,8 +66,7 @@ private: ASTPointer<StructDefinition> parseStructDefinition(); ASTPointer<EnumDefinition> parseEnumDefinition(); ASTPointer<EnumValue> parseEnumValue(); - ASTPointer<VariableDeclaration> parseVariableDeclaration( - VarDeclParserOptions const& _options = VarDeclParserOptions(), + ASTPointer<VariableDeclaration> parseVariableDeclaration(VarDeclParserOptions const& _options = VarDeclParserOptions(), ASTPointer<TypeName> const& _lookAheadArrayType = ASTPointer<TypeName>()); ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<EventDefinition> parseEventDefinition(); @@ -516,7 +516,7 @@ private: std::vector<std::string> m_parameterNames; std::vector<std::string> m_returnParameterNames; Location const m_location; - /// true iff the function takes an arbitrary number of arguments of arbitrary types + /// true if the function takes an arbitrary number of arguments of arbitrary types bool const m_arbitraryParameters = false; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack |