From 638960f22f5c5d394dce0bb8223b860e4869b8c7 Mon Sep 17 00:00:00 2001 From: Christian Date: Thu, 20 Nov 2014 18:33:23 +0100 Subject: [PATCH] Contracts as types and framework for special global variables. --- libsolidity/AST.cpp | 36 ++++++++++++- libsolidity/AST.h | 20 +++++--- libsolidity/CompilerStack.cpp | 5 +- libsolidity/CompilerStack.h | 9 ++-- libsolidity/ExpressionCompiler.cpp | 14 ++++-- libsolidity/GlobalContext.cpp | 70 ++++++++++++++++++++++++++ libsolidity/GlobalContext.h | 61 ++++++++++++++++++++++ libsolidity/NameAndTypeResolver.cpp | 43 +++++----------- libsolidity/NameAndTypeResolver.h | 3 +- libsolidity/Scanner.cpp | 1 - libsolidity/Token.h | 1 - libsolidity/Types.cpp | 30 +++++++++-- libsolidity/Types.h | 32 +++++++----- test/solidityCompiler.cpp | 2 +- test/solidityEndToEndTest.cpp | 29 +++++++---- test/solidityExpressionCompiler.cpp | 2 +- test/solidityNameAndTypeResolution.cpp | 2 +- test/solidityParser.cpp | 20 ++++++++ 18 files changed, 302 insertions(+), 78 deletions(-) create mode 100644 libsolidity/GlobalContext.cpp create mode 100644 libsolidity/GlobalContext.h diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 9aecbdedf..22a658d40 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -51,6 +51,40 @@ void StructDefinition::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } +void StructDefinition::checkValidityOfMembers() +{ + checkMemberTypes(); + checkRecursion(); +} + +void StructDefinition::checkMemberTypes() +{ + for (ASTPointer const& member: getMembers()) + if (!member->getType()->canBeStored()) + BOOST_THROW_EXCEPTION(member->createTypeError("Type cannot be used in struct.")); +} + +void StructDefinition::checkRecursion() +{ + set definitionsSeen; + vector queue = {this}; + while (!queue.empty()) + { + StructDefinition const* def = queue.back(); + queue.pop_back(); + if (definitionsSeen.count(def)) + BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) + << errinfo_comment("Recursive struct definition.")); + definitionsSeen.insert(def); + for (ASTPointer const& member: def->getMembers()) + if (member->getType()->getCategory() == Type::Category::STRUCT) + { + UserDefinedTypeName const& typeName = dynamic_cast(*member->getTypeName()); + queue.push_back(&dynamic_cast(*typeName.getReferencedDeclaration())); + } + } +} + void ParameterList::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) @@ -258,7 +292,7 @@ void Literal::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } -TypeError ASTNode::createTypeError(string const& _description) +TypeError ASTNode::createTypeError(string const& _description) const { return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description); } diff --git a/libsolidity/AST.h b/libsolidity/AST.h index 80c7dd198..01e1b54f0 100644 --- a/libsolidity/AST.h +++ b/libsolidity/AST.h @@ -66,7 +66,7 @@ public: /// Creates a @ref TypeError exception and decorates it with the location of the node and /// the given description - TypeError createTypeError(std::string const& _description); + TypeError createTypeError(std::string const& _description) const; ///@{ ///@name equality operators @@ -139,7 +139,14 @@ public: std::vector> const& getMembers() const { return m_members; } + /// Checks that the members do not include any recursive structs and have valid types + /// (e.g. no functions). + void checkValidityOfMembers(); + private: + void checkMemberTypes(); + void checkRecursion(); + std::vector> m_members; }; @@ -210,7 +217,6 @@ public: Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; - bool isTypeGivenExplicitly() const { return bool(m_typeName); } TypeName* getTypeName() const { return m_typeName.get(); } /// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly @@ -238,6 +244,7 @@ public: /// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared /// pointer until the types have been resolved using the @ref NameAndTypeResolver. + /// If it returns an empty shared pointer after that, this indicates that the type was not found. virtual std::shared_ptr toType() const = 0; }; @@ -263,8 +270,7 @@ private: }; /** - * Name referring to a user-defined type (i.e. a struct). - * @todo some changes are necessary if this is also used to refer to contract types later + * Name referring to a user-defined type (i.e. a struct, contract, etc.). */ class UserDefinedTypeName: public TypeName { @@ -275,13 +281,13 @@ public: virtual std::shared_ptr toType() const override { return Type::fromUserDefinedTypeName(*this); } ASTString const& getName() const { return *m_name; } - void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } - StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } + void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } private: ASTPointer m_name; - StructDefinition* m_referencedStruct; + Declaration* m_referencedDeclaration; }; /** diff --git a/libsolidity/CompilerStack.cpp b/libsolidity/CompilerStack.cpp index d87c27916..f051d58fd 100644 --- a/libsolidity/CompilerStack.cpp +++ b/libsolidity/CompilerStack.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,9 @@ void CompilerStack::parse() if (!m_scanner) BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Source not available.")); m_contractASTNode = Parser().parse(m_scanner); - NameAndTypeResolver().resolveNamesAndTypes(*m_contractASTNode); + m_globalContext = make_shared(); + m_globalContext->setCurrentContract(*m_contractASTNode); + NameAndTypeResolver(m_globalContext->getDeclarations()).resolveNamesAndTypes(*m_contractASTNode); m_parseSuccessful = true; } diff --git a/libsolidity/CompilerStack.h b/libsolidity/CompilerStack.h index 2fb505897..6cae8660f 100644 --- a/libsolidity/CompilerStack.h +++ b/libsolidity/CompilerStack.h @@ -30,9 +30,11 @@ namespace dev { namespace solidity { -class Scanner; // forward -class ContractDefinition; // forward -class Compiler; // forward +// forward declarations +class Scanner; +class ContractDefinition; +class Compiler; +class GlobalContext; /** * Easy to use and self-contained Solidity compiler with as few header dependencies as possible. @@ -71,6 +73,7 @@ public: private: std::shared_ptr m_scanner; + std::shared_ptr m_globalContext; std::shared_ptr m_contractASTNode; bool m_parseSuccessful; std::string m_interface; diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 89eeb31f2..4d175527a 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -167,7 +167,15 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall) BOOST_THROW_EXCEPTION(InternalCompilerError()); Expression& firstArgument = *_functionCall.getArguments().front(); firstArgument.accept(*this); - appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); + if (firstArgument.getType()->getCategory() == Type::Category::CONTRACT && + _functionCall.getType()->getCategory() == Type::Category::INTEGER) + { + // explicit type conversion contract -> address, nothing to do. + } + else + { + appendTypeConversion(*firstArgument.getType(), *_functionCall.getType()); + } } else { @@ -466,7 +474,7 @@ void ExpressionCompiler::LValue::storeValue(Expression const& _expression, bool } } -void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Expression& _expression) +void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(Expression const& _expression) { if (!_expression.lvalueRequested()) { @@ -475,7 +483,7 @@ void ExpressionCompiler::LValue::retrieveValueIfLValueNotRequested(const Express } } -void ExpressionCompiler::LValue::fromDeclaration( Expression const& _expression, Declaration const& _declaration) +void ExpressionCompiler::LValue::fromDeclaration(Expression const& _expression, Declaration const& _declaration) { if (m_context->isLocalVariable(&_declaration)) { diff --git a/libsolidity/GlobalContext.cpp b/libsolidity/GlobalContext.cpp new file mode 100644 index 000000000..6179722b2 --- /dev/null +++ b/libsolidity/GlobalContext.cpp @@ -0,0 +1,70 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Container of the (implicit and explicit) global objects. + */ + +#include +#include +#include +#include + +using namespace std; + +namespace dev +{ +namespace solidity +{ + +GlobalContext::GlobalContext() +{ + // CurrentContract this; // @todo type depends on context -> switch prior to entering contract + // Message msg; + // Transaction tx; + // Block block; + + //@todo type will be a custom complex type, maybe the same type class for msg tx and block. + //addVariable("msg", ); +} + +void GlobalContext::setCurrentContract(ContractDefinition const& _contract) +{ + m_this = createVariable("this", make_shared(_contract)); +} + +vector GlobalContext::getDeclarations() const +{ + vector declarations; + declarations.reserve(m_objects.size() + 1); + for (ASTPointer const& declaration: m_objects) + declarations.push_back(declaration.get()); + declarations.push_back(m_this.get()); + return declarations; +} + +ASTPointer GlobalContext::createVariable(const string& _name, shared_ptr const& _type) +{ + ASTPointer variable = make_shared(Location(), ASTPointer(), + make_shared(_name)); + variable->setType(_type); + return variable; +} + +} +} diff --git a/libsolidity/GlobalContext.h b/libsolidity/GlobalContext.h new file mode 100644 index 000000000..b6dea7d56 --- /dev/null +++ b/libsolidity/GlobalContext.h @@ -0,0 +1,61 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2014 + * Container of the (implicit and explicit) global objects. + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace dev +{ +namespace solidity +{ + +class Type; // forward + +/** + * Container for all global objects which look like AST nodes, but are not part of the AST + * that is currently being compiled. + * @note must not be destroyed or moved during compilation as its objects can be referenced from + * other objects. + */ +class GlobalContext: private boost::noncopyable +{ +public: + GlobalContext(); + void setCurrentContract(ContractDefinition const& _contract); + + std::vector getDeclarations() const; + +private: + /// Creates a virtual variable declaration with the given name and type. + static ASTPointer createVariable(std::string const& _name, std::shared_ptr const& _type); + + std::vector> m_objects; + ASTPointer m_this; +}; + +} +} diff --git a/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index aa7726582..225f2a78a 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/NameAndTypeResolver.cpp @@ -32,15 +32,20 @@ namespace solidity { +NameAndTypeResolver::NameAndTypeResolver(std::vector const& _globals) +{ + for (Declaration* declaration: _globals) + m_scopes[nullptr].registerDeclaration(*declaration); +} + void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) { - reset(); DeclarationRegistrationHelper registrar(m_scopes, _contract); m_currentScope = &m_scopes[&_contract]; for (ASTPointer const& structDef: _contract.getDefinedStructs()) ReferencesResolver resolver(*structDef, *this, nullptr); for (ASTPointer const& structDef: _contract.getDefinedStructs()) - checkForRecursion(*structDef); + structDef->checkValidityOfMembers(); for (ASTPointer const& variable: _contract.getStateVariables()) ReferencesResolver resolver(*variable, *this, nullptr); for (ASTPointer const& function: _contract.getDefinedFunctions()) @@ -73,30 +78,6 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name return m_currentScope->resolveName(_name, _recursive); } -void NameAndTypeResolver::checkForRecursion(StructDefinition const& _struct) -{ - set definitionsSeen; - vector queue = {&_struct}; - while (!queue.empty()) - { - StructDefinition const* def = queue.back(); - queue.pop_back(); - if (definitionsSeen.count(def)) - BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation()) - << errinfo_comment("Recursive struct definition.")); - definitionsSeen.insert(def); - for (ASTPointer const& member: def->getMembers()) - if (member->getType()->getCategory() == Type::Category::STRUCT) - queue.push_back(dynamic_cast(*member->getTypeName()).getReferencedStruct()); - } -} - -void NameAndTypeResolver::reset() -{ - m_scopes.clear(); - m_currentScope = nullptr; -} - DeclarationRegistrationHelper::DeclarationRegistrationHelper(map& _scopes, ASTNode& _astRoot): m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) @@ -195,7 +176,11 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable) // endVisit because the internal type needs resolving if it is a user defined type // or mapping if (_variable.getTypeName()) + { _variable.setType(_variable.getTypeName()->toType()); + if (!_variable.getType()) + BOOST_THROW_EXCEPTION(_variable.getTypeName()->createTypeError("Invalid type name")); + } else if (!m_allowLazyTypes) BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed.")); // otherwise we have a "var"-declaration whose type is resolved by the first assignment @@ -221,11 +206,7 @@ bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) if (!declaration) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_typeName.getLocation()) << errinfo_comment("Undeclared identifier.")); - StructDefinition* referencedStruct = dynamic_cast(declaration); - //@todo later, contracts are also valid types - if (!referencedStruct) - BOOST_THROW_EXCEPTION(_typeName.createTypeError("Identifier does not name a type name.")); - _typeName.setReferencedStruct(*referencedStruct); + _typeName.setReferencedDeclaration(*declaration); return false; } diff --git a/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index d335807e5..64f3c89db 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/NameAndTypeResolver.h @@ -41,8 +41,7 @@ namespace solidity class NameAndTypeResolver: private boost::noncopyable { public: - NameAndTypeResolver() {} - + explicit NameAndTypeResolver(std::vector const& _globals); void resolveNamesAndTypes(ContractDefinition& _contract); /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, diff --git a/libsolidity/Scanner.cpp b/libsolidity/Scanner.cpp index 895b8ae7a..dd18a320f 100644 --- a/libsolidity/Scanner.cpp +++ b/libsolidity/Scanner.cpp @@ -683,7 +683,6 @@ Token::Value Scanner::scanNumber(char _charSeen) KEYWORD("switch", Token::SWITCH) \ KEYWORD_GROUP('t') \ KEYWORD("text", Token::TEXT) \ - KEYWORD("this", Token::THIS) \ KEYWORD("true", Token::TRUE_LITERAL) \ KEYWORD_GROUP('u') \ KEYWORD("uint", Token::UINT) \ diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 524487521..8974ca1ad 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -160,7 +160,6 @@ namespace solidity K(RETURNS, "returns", 0) \ K(STRUCT, "struct", 0) \ K(SWITCH, "switch", 0) \ - K(THIS, "this", 0) \ K(VAR, "var", 0) \ K(WHILE, "while", 0) \ \ diff --git a/libsolidity/Types.cpp b/libsolidity/Types.cpp index 897ca221c..f9d3d90fa 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/Types.cpp @@ -60,13 +60,24 @@ shared_ptr Type::fromElementaryTypeName(Token::Value _typeToken) shared_ptr Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) { - return make_shared(*_typeName.getReferencedStruct()); + Declaration const* declaration = _typeName.getReferencedDeclaration(); + if (StructDefinition const* structDef = dynamic_cast(declaration)) + return make_shared(*structDef); + else if (FunctionDefinition const* function = dynamic_cast(declaration)) + return make_shared(*function); + else if (ContractDefinition const* contract = dynamic_cast(declaration)) + return make_shared(*contract); + return shared_ptr(); } shared_ptr Type::fromMapping(Mapping const& _typeName) { shared_ptr keyType = _typeName.getKeyType().toType(); + if (!keyType) + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name.")); shared_ptr valueType = _typeName.getValueType().toType(); + if (!valueType) + BOOST_THROW_EXCEPTION(_typeName.getValueType().createTypeError("Invalid type name")); return make_shared(keyType, valueType); } @@ -201,6 +212,15 @@ u256 BoolType::literalValue(Literal const& _literal) const BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Bool type constructed from non-boolean literal.")); } +bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (isImplicitlyConvertibleTo(_convertTo)) + return true; + if (_convertTo.getCategory() == Category::INTEGER) + return dynamic_cast(_convertTo).isAddress(); + return false; +} + bool ContractType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -217,6 +237,11 @@ u256 ContractType::getStorageSize() const return max(1, size); } +string ContractType::toString() const +{ + return "contract " + m_contract.getName(); +} + bool StructType::operator==(Type const& _other) const { if (_other.getCategory() != getCategory()) @@ -282,8 +307,7 @@ bool FunctionType::operator==(Type const& _other) const string FunctionType::toString() const { - //@todo nice string for function types - return "function(...)returns(...)"; + return "function " + m_function.getName(); } bool MappingType::operator==(Type const& _other) const diff --git a/libsolidity/Types.h b/libsolidity/Types.h index 2208a880e..297284ba0 100644 --- a/libsolidity/Types.h +++ b/libsolidity/Types.h @@ -106,6 +106,8 @@ public: /// @returns number of bytes required to hold this value in storage. /// For dynamically "allocated" types, it returns the size of the statically allocated head, virtual u256 getStorageSize() const { return 1; } + /// Returns true if the type can be stored in storage. + virtual bool canBeStored() const { return true; } /// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping. virtual bool canLiveOutsideStorage() const { return true; } @@ -151,7 +153,7 @@ public: virtual bool operator==(Type const& _other) const override; - virtual unsigned getCalldataEncodedSize() const { return m_bits / 8; } + virtual unsigned getCalldataEncodedSize() const override { return m_bits / 8; } virtual std::string toString() const override; virtual u256 literalValue(Literal const& _literal) const override; @@ -197,10 +199,11 @@ class ContractType: public Type public: virtual Category getCategory() const override { return Category::CONTRACT; } ContractType(ContractDefinition const& _contract): m_contract(_contract) {} - + /// Contracts can be converted to themselves and to addresses. + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const; - virtual std::string toString() const override { return "contract{...}"; } + virtual u256 getStorageSize() const override; + virtual std::string toString() const override; private: ContractDefinition const& m_contract; @@ -220,8 +223,8 @@ public: } virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const; - virtual bool canLiveOutsideStorage() const; + virtual u256 getStorageSize() const override; + virtual bool canLiveOutsideStorage() const override; virtual std::string toString() const override; virtual MemberList const& getMembers() const override; @@ -247,8 +250,9 @@ public: virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; - virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canBeStored() const override { return false; } + virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); } + virtual bool canLiveOutsideStorage() const override { return false; } private: FunctionDefinition const& m_function; @@ -266,7 +270,7 @@ public: virtual bool operator==(Type const& _other) const override; virtual std::string toString() const override; - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canLiveOutsideStorage() const override { return false; } std::shared_ptr getKeyType() const { return m_keyType; } std::shared_ptr getValueType() const { return m_valueType; } @@ -287,8 +291,9 @@ public: VoidType() {} virtual std::string toString() const override { return "void"; } - virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canBeStored() const override { return false; } + virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable void type requested.")); } + virtual bool canLiveOutsideStorage() const override { return false; } }; /** @@ -304,8 +309,9 @@ public: std::shared_ptr const& getActualType() const { return m_actualType; } virtual bool operator==(Type const& _other) const override; - virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } - virtual bool canLiveOutsideStorage() const { return false; } + virtual bool canBeStored() const override { return false; } + virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } + virtual bool canLiveOutsideStorage() const override { return false; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } private: diff --git a/test/solidityCompiler.cpp b/test/solidityCompiler.cpp index e0635b6ae..1767a0f36 100644 --- a/test/solidityCompiler.cpp +++ b/test/solidityCompiler.cpp @@ -48,7 +48,7 @@ bytes compileContract(const string& _sourceCode) Parser parser; ASTPointer contract; BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver; + NameAndTypeResolver resolver({}); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); Compiler compiler; diff --git a/test/solidityEndToEndTest.cpp b/test/solidityEndToEndTest.cpp index 4e68103ac..a7c6357bf 100644 --- a/test/solidityEndToEndTest.cpp +++ b/test/solidityEndToEndTest.cpp @@ -45,17 +45,17 @@ class ExecutionFramework public: ExecutionFramework() { g_logVerbosity = 0; } - bytes const& compileAndRun(string const& _sourceCode) + bytes const& compileAndRun(string const& _sourceCode, u256 const& _value = 0) { bytes code = dev::solidity::CompilerStack::staticCompile(_sourceCode); - sendMessage(code, true); + sendMessage(code, true, _value); BOOST_REQUIRE(!m_output.empty()); return m_output; } - bytes const& callContractFunction(byte _index, bytes const& _data = bytes()) + bytes const& callContractFunction(byte _index, bytes const& _data = bytes(), u256 const& _value = 0) { - sendMessage(bytes(1, _index) + _data, false); + sendMessage(bytes(1, _index) + _data, false, _value); return m_output; } @@ -111,11 +111,11 @@ private: return toBigEndian(_cppFunction(_arguments...)); } - void sendMessage(bytes const& _data, bool _isCreation) + void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0) { eth::Executive executive(m_state); - eth::Transaction t = _isCreation ? eth::Transaction(0, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) - : eth::Transaction(0, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); + eth::Transaction t = _isCreation ? eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) + : eth::Transaction(_value, m_gasPrice, m_gas, m_contractAddress, _data, 0, KeyPair::create().sec()); bytes transactionRLP = t.rlp(); try { @@ -125,7 +125,7 @@ private: catch (...) {} if (_isCreation) { - BOOST_REQUIRE(!executive.create(Address(), 0, m_gasPrice, m_gas, &_data, Address())); + BOOST_REQUIRE(!executive.create(Address(), _value, m_gasPrice, m_gas, &_data, Address())); m_contractAddress = executive.newAddress(); BOOST_REQUIRE(m_contractAddress); BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); @@ -133,7 +133,7 @@ private: else { BOOST_REQUIRE(m_state.addressHasCode(m_contractAddress)); - BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), 0, m_gasPrice, &_data, m_gas, Address())); + BOOST_REQUIRE(!executive.call(m_contractAddress, Address(), _value, m_gasPrice, &_data, m_gas, Address())); } BOOST_REQUIRE(executive.go()); executive.finalize(); @@ -722,6 +722,17 @@ BOOST_AUTO_TEST_CASE(constructor) testSolidityAgainstCpp(0, get, u256(7)); } +BOOST_AUTO_TEST_CASE(balance) +{ + char const* sourceCode = "contract test {\n" + " function getBalance() returns (u256 balance) {\n" + " return address(this).balance;\n" + " }\n" + "}\n"; + compileAndRun(sourceCode, 23); + BOOST_CHECK(callContractFunction(0) == toBigEndian(u256(23))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/solidityExpressionCompiler.cpp b/test/solidityExpressionCompiler.cpp index 59a9e9336..6ea66badb 100644 --- a/test/solidityExpressionCompiler.cpp +++ b/test/solidityExpressionCompiler.cpp @@ -88,7 +88,7 @@ bytes compileFirstExpression(const string& _sourceCode, vector> _ Parser parser; ASTPointer contract; BOOST_REQUIRE_NO_THROW(contract = parser.parse(make_shared(CharStream(_sourceCode)))); - NameAndTypeResolver resolver; + NameAndTypeResolver resolver({}); BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); FirstExpressionExtractor extractor(*contract); BOOST_REQUIRE(extractor.getExpression() != nullptr); diff --git a/test/solidityNameAndTypeResolution.cpp b/test/solidityNameAndTypeResolution.cpp index 930bba0e3..61aabb346 100644 --- a/test/solidityNameAndTypeResolution.cpp +++ b/test/solidityNameAndTypeResolution.cpp @@ -43,7 +43,7 @@ void parseTextAndResolveNames(std::string const& _source) Parser parser; ASTPointer contract = parser.parse( std::make_shared(CharStream(_source))); - NameAndTypeResolver resolver; + NameAndTypeResolver resolver({}); resolver.resolveNamesAndTypes(*contract); } } diff --git a/test/solidityParser.cpp b/test/solidityParser.cpp index 9319a02c5..0ee88b261 100644 --- a/test/solidityParser.cpp +++ b/test/solidityParser.cpp @@ -221,6 +221,26 @@ BOOST_AUTO_TEST_CASE(statement_starting_with_type_conversion) BOOST_CHECK_NO_THROW(parseText(text)); } +BOOST_AUTO_TEST_CASE(blockchain_access) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " u256 x = address(0).balance;\n" + " }\n" + "}\n"; + BOOST_CHECK_NO_THROW(parseText(text)); +} + +BOOST_AUTO_TEST_CASE(blockchain_access_invalid) +{ + char const* text = "contract test {\n" + " function fun() {\n" + " address(0).balance = 7;\n" + " }\n" + "}\n"; + BOOST_CHECK_THROW(parseText(text), TypeError); +} + BOOST_AUTO_TEST_SUITE_END() }