diff options
-rw-r--r-- | docs/contracts.rst | 384 | ||||
-rw-r--r-- | docs/control-structures.rst | 2 | ||||
-rw-r--r-- | docs/introduction-to-smart-contracts.rst | 2 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 2 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 12 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 2 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 32 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 1 | ||||
-rw-r--r-- | test/libsolidity/Imports.cpp | 2 |
9 files changed, 221 insertions, 218 deletions
diff --git a/docs/contracts.rst b/docs/contracts.rst index 1d292c25..45391cd9 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -61,65 +61,65 @@ This means that cyclic creation dependencies are impossible. :: contract OwnedToken { - // TokenCreator is a contract type that is defined below. - // It is fine to reference it as long as it is not used - // to create a new contract. - TokenCreator creator; - address owner; - bytes32 name; - // This is the constructor which registers the - // creator and the assigned name. - function OwnedToken(bytes32 _name) { - owner = msg.sender; - // We do an explicit type conversion from `address` - // to `TokenCreator` and assume that the type of - // the calling contract is TokenCreator, there is - // no real way to check that. - creator = TokenCreator(msg.sender); - name = _name; - } - function changeName(bytes32 newName) { - // Only the creator can alter the name -- - // the comparison is possible since contracts - // are implicitly convertible to addresses. - if (msg.sender == creator) name = newName; - } - function transfer(address newOwner) { - // Only the current owner can transfer the token. - if (msg.sender != owner) return; - // We also want to ask the creator if the transfer - // is fine. Note that this calls a function of the - // contract defined below. If the call fails (e.g. - // due to out-of-gas), the execution here stops - // immediately. - if (creator.isTokenTransferOK(owner, newOwner)) - owner = newOwner; - } + // TokenCreator is a contract type that is defined below. + // It is fine to reference it as long as it is not used + // to create a new contract. + TokenCreator creator; + address owner; + bytes32 name; + // This is the constructor which registers the + // creator and the assigned name. + function OwnedToken(bytes32 _name) { + owner = msg.sender; + // We do an explicit type conversion from `address` + // to `TokenCreator` and assume that the type of + // the calling contract is TokenCreator, there is + // no real way to check that. + creator = TokenCreator(msg.sender); + name = _name; + } + function changeName(bytes32 newName) { + // Only the creator can alter the name -- + // the comparison is possible since contracts + // are implicitly convertible to addresses. + if (msg.sender == creator) name = newName; + } + function transfer(address newOwner) { + // Only the current owner can transfer the token. + if (msg.sender != owner) return; + // We also want to ask the creator if the transfer + // is fine. Note that this calls a function of the + // contract defined below. If the call fails (e.g. + // due to out-of-gas), the execution here stops + // immediately. + if (creator.isTokenTransferOK(owner, newOwner)) + owner = newOwner; + } } contract TokenCreator { - function createToken(bytes32 name) + function createToken(bytes32 name) returns (OwnedToken tokenAddress) - { - // Create a new Token contract and return its address. - // From the JavaScript side, the return type is simply - // "address", as this is the closest type available in - // the ABI. - return new OwnedToken(name); - } - function changeName(OwnedToken tokenAddress, bytes32 name) { - // Again, the external type of "tokenAddress" is - // simply "address". - tokenAddress.changeName(name); - } - function isTokenTransferOK( - address currentOwner, - address newOwner - ) returns (bool ok) { - // Check some arbitrary condition. - address tokenAddress = msg.sender; - return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); - } + { + // Create a new Token contract and return its address. + // From the JavaScript side, the return type is simply + // "address", as this is the closest type available in + // the ABI. + return new OwnedToken(name); + } + function changeName(OwnedToken tokenAddress, bytes32 name) { + // Again, the external type of "tokenAddress" is + // simply "address". + tokenAddress.changeName(name); + } + function isTokenTransferOK( + address currentOwner, + address newOwner + ) returns (bool ok) { + // Check some arbitrary condition. + address tokenAddress = msg.sender; + return (sha3(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff); + } } .. index:: ! visibility, external, public, private, internal @@ -179,9 +179,9 @@ return parameter list for functions. :: contract c { - function f(uint a) private returns (uint b) { return a + 1; } - function setData(uint a) internal { data = a; } - uint public data; + function f(uint a) private returns (uint b) { return a + 1; } + function setData(uint a) internal { data = a; } + uint public data; } Other contracts can call `c.data()` to retrieve the value of @@ -209,7 +209,7 @@ it is a state variable and if it is accessed externally :: contract test { - uint public data = 42; + uint public data = 42; } The next example is a bit more complex: @@ -217,16 +217,16 @@ The next example is a bit more complex: :: contract complex { - struct Data { uint a; bytes3 b; mapping(uint => uint) map; } - mapping(uint => mapping(bool => Data[])) public data; + struct Data { uint a; bytes3 b; mapping(uint => uint) map; } + mapping(uint => mapping(bool => Data[])) public data; } It will generate a function of the following form:: function data(uint arg1, bool arg2, uint arg3) returns (uint a, bytes3 b) { - a = data[arg1][arg2][arg3].a; - b = data[arg1][arg2][arg3].b; + a = data[arg1][arg2][arg3].a; + b = data[arg1][arg2][arg3].b; } Note that the mapping in the struct is omitted because there @@ -247,41 +247,41 @@ inheritable properties of contracts and may be overridden by derived contracts. :: contract owned { - function owned() { owner = msg.sender; } - address owner; - - // This contract only defines a modifier but does not use - // it - it will be used in derived contracts. - // The function body is inserted where the special symbol - // "_" in the definition of a modifier appears. - // This means that if the owner calls this function, the - // function is executed and otherwise, an exception is - // thrown. - modifier onlyowner { if (msg.sender != owner) throw; _ } + function owned() { owner = msg.sender; } + address owner; + + // This contract only defines a modifier but does not use + // it - it will be used in derived contracts. + // The function body is inserted where the special symbol + // "_" in the definition of a modifier appears. + // This means that if the owner calls this function, the + // function is executed and otherwise, an exception is + // thrown. + modifier onlyowner { if (msg.sender != owner) throw; _ } } contract mortal is owned { - // This contract inherits the "onlyowner"-modifier from - // "owned" and applies it to the "close"-function, which - // causes that calls to "close" only have an effect if - // they are made by the stored owner. - function close() onlyowner { - selfdestruct(owner); - } + // This contract inherits the "onlyowner"-modifier from + // "owned" and applies it to the "close"-function, which + // causes that calls to "close" only have an effect if + // they are made by the stored owner. + function close() onlyowner { + selfdestruct(owner); + } } contract priced { - // Modifiers can receive arguments: - modifier costs(uint price) { if (msg.value >= price) _ } + // Modifiers can receive arguments: + modifier costs(uint price) { if (msg.value >= price) _ } } contract Register is priced, owned { - mapping (address => bool) registeredAddresses; - uint price; - function Register(uint initialPrice) { price = initialPrice; } - function register() costs(price) { - registeredAddresses[msg.sender] = true; - } - function changePrice(uint _price) onlyowner { - price = _price; - } + mapping (address => bool) registeredAddresses; + uint price; + function Register(uint initialPrice) { price = initialPrice; } + function register() costs(price) { + registeredAddresses[msg.sender] = true; + } + function changePrice(uint _price) onlyowner { + price = _price; + } } Multiple modifiers can be applied to a function by specifying them in a @@ -305,8 +305,8 @@ for array and struct types and not possible for mapping types). :: contract C { - uint constant x = 32**22 + 8; - string constant text = "abc"; + uint constant x = 32**22 + 8; + string constant text = "abc"; } This has the effect that the compiler does not reserve a storage slot @@ -336,24 +336,24 @@ possible. :: contract Test { - function() { x = 1; } - uint x; + function() { x = 1; } + uint x; } // This contract rejects any Ether sent to it. It is good // practise to include such a function for every contract // in order not to loose Ether. contract Rejector { - function() { throw; } + function() { throw; } } contract Caller { function callTest(address testAddress) { - Test(testAddress).call(0xabcdef01); // hash does not exist - // results in Test(testAddress).x becoming == 1. - Rejector r = Rejector(0x123); - r.send(2 ether); - // results in r.balance == 0 + Test(testAddress).call(0xabcdef01); // hash does not exist + // results in Test(testAddress).x becoming == 1. + Rejector r = Rejector(0x123); + r.send(2 ether); + // results in r.balance == 0 } } @@ -400,17 +400,17 @@ All non-indexed arguments will be stored in the data part of the log. :: contract ClientReceipt { - event Deposit( - address indexed _from, - bytes32 indexed _id, - uint _value - ); - function deposit(bytes32 _id) { - // Any call to this function (even deeply nested) can - // be detected from the JavaScript API by filtering - // for `Deposit` to be called. - Deposit(msg.sender, _id, msg.value); - } + event Deposit( + address indexed _from, + bytes32 indexed _id, + uint _value + ); + function deposit(bytes32 _id) { + // Any call to this function (even deeply nested) can + // be detected from the JavaScript API by filtering + // for `Deposit` to be called. + Deposit(msg.sender, _id, msg.value); + } } The use in the JavaScript API would be as follows: @@ -425,17 +425,17 @@ The use in the JavaScript API would be as follows: // watch for changes event.watch(function(error, result){ - // result will contain various information - // including the argumets given to the Deposit - // call. - if (!error) - console.log(result); + // result will contain various information + // including the argumets given to the Deposit + // call. + if (!error) + console.log(result); }); // Or pass a callback to start watching immediately var event = clientReceipt.Deposit(function(error, result) { - if (!error) - console.log(result); + if (!error) + console.log(result); }); .. index:: ! log @@ -452,10 +452,10 @@ as topics. The event call above can be performed in the same way as :: log3( - msg.value, - 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20, - msg.sender, - _id + msg.value, + 0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20, + msg.sender, + _id ); where the long hexadecimal number is equal to @@ -502,7 +502,7 @@ Details are given in the following example. // accessed externally via `this`, though. contract mortal is owned { function kill() { - if (msg.sender == owner) selfdestruct(owner); + if (msg.sender == owner) selfdestruct(owner); } } @@ -611,12 +611,12 @@ Derived contracts need to provide all arguments needed for the base constructors. This can be done at two places:: contract Base { - uint x; - function Base(uint _x) { x = _x; } + uint x; + function Base(uint _x) { x = _x; } } contract Derived is Base(7) { - function Derived(uint _y) Base(_y * _y) { - } + function Derived(uint _y) Base(_y * _y) { + } } Either directly in the inheritance list (`is Base(7)`) or in @@ -667,13 +667,13 @@ Abstract Contracts Contract functions can lack an implementation as in the following example (note that the function declaration header is terminated by `;`):: contract feline { - function utterance() returns (bytes32); + function utterance() returns (bytes32); } Such contracts cannot be compiled (even if they contain implemented functions alongside non-implemented functions), but they can be used as base contracts:: contract Cat is feline { - function utterance() returns (bytes32) { return "miaow"; } + function utterance() returns (bytes32) { return "miaow"; } } If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. @@ -716,35 +716,35 @@ more advanced example to implement a set). function insert(Data storage self, uint value) returns (bool) { - if (self.flags[value]) - return false; // already there - self.flags[value] = true; - return true; + if (self.flags[value]) + return false; // already there + self.flags[value] = true; + return true; } function remove(Data storage self, uint value) - returns (bool) + returns (bool) { - if (!self.flags[value]) - return false; // not there - self.flags[value] = false; - return true; + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; } function contains(Data storage self, uint value) - returns (bool) + returns (bool) { - return self.flags[value]; + return self.flags[value]; } } contract C { - Set.Data knownValues; - function register(uint value) { - // The library functions can be called without a - // specific instance of the library, since the - // "instance" will be the current contract. - if (!Set.insert(knownValues, value)) - throw; - } - // In this contract, we can also directly access knownValues.flags, if we want. + Set.Data knownValues; + function register(uint value) { + // The library functions can be called without a + // specific instance of the library, since the + // "instance" will be the current contract. + if (!Set.insert(knownValues, value)) + throw; + } + // In this contract, we can also directly access knownValues.flags, if we want. } Of course, you do not have to follow this way to use @@ -774,8 +774,8 @@ encoding of the address of the library contract. Restrictions for libraries in comparison to contracts: - - no state variables - - cannot inherit nor be inherited +- no state variables +- cannot inherit nor be inherited (these might be lifted at a later point) @@ -845,63 +845,63 @@ Let us rewrite the set example from the function insert(Data storage self, uint value) returns (bool) { - if (self.flags[value]) - return false; // already there - self.flags[value] = true; - return true; + if (self.flags[value]) + return false; // already there + self.flags[value] = true; + return true; } function remove(Data storage self, uint value) - returns (bool) + returns (bool) { - if (!self.flags[value]) - return false; // not there - self.flags[value] = false; - return true; + if (!self.flags[value]) + return false; // not there + self.flags[value] = false; + return true; } function contains(Data storage self, uint value) - returns (bool) + returns (bool) { - return self.flags[value]; + return self.flags[value]; } } contract C { - using Set for Set.Data; // this is the crucial change - Set.Data knownValues; - function register(uint value) { - // Here, all variables of type Set.Data have - // corresponding member functions. - // The following function call is identical to - // Set.insert(knownValues, value) - if (!knownValues.insert(value)) - throw; - } + using Set for Set.Data; // this is the crucial change + Set.Data knownValues; + function register(uint value) { + // Here, all variables of type Set.Data have + // corresponding member functions. + // The following function call is identical to + // Set.insert(knownValues, value) + if (!knownValues.insert(value)) + throw; + } } It is also possible to extend elementary types in that way:: library Search { - function indexOf(uint[] storage self, uint value) { - for (uint i = 0; i < self.length; i++) - if (self[i] == value) return i; - return uint(-1); - } + function indexOf(uint[] storage self, uint value) { + for (uint i = 0; i < self.length; i++) + if (self[i] == value) return i; + return uint(-1); + } } contract C { - using Search for uint[]; - uint[] data; - function append(uint value) { - data.push(value); - } - function replace(uint _old, uint _new) { - // This performs the library function call - uint index = data.find(_old); - if (index == -1) - data.push(_new); - else - data[index] = _new; - } + using Search for uint[]; + uint[] data; + function append(uint value) { + data.push(value); + } + function replace(uint _old, uint _new) { + // This performs the library function call + uint index = data.find(_old); + if (index == -1) + data.push(_new); + else + data[index] = _new; + } } Note that all library calls are actual EVM function calls. This means that diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 4f8d3b93..c833fbfc 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -16,7 +16,7 @@ Parentheses can *not* be omitted for conditionals, but curly brances can be omit around single-statement bodies. Note that there is no type conversion from non-boolean to boolean types as -there is in C and JavaScript, so `if (1) { ... }` is _not_ valid Solidity. +there is in C and JavaScript, so `if (1) { ... }` is *not* valid Solidity. .. index:: ! function;call, function;internal, function;external diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index b0f1896a..711e7082 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -100,7 +100,7 @@ registering with username and password - all you need is an Ethereum keypair. } } -This contract introduces some new concepts, let us go through them one by one +This contract introduces some new concepts, let us go through them one by one. The line `address public minter;` declares a state variable of type address that is publicly accessible. The `address` type is a 160 bit value diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 92347bfc..96ffdd6e 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -264,7 +264,7 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract) list<list<ContractDefinition const*>> input(1, {}); for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts()) { - Identifier const& baseName = baseSpecifier->name(); + UserDefinedTypeName const& baseName = baseSpecifier->name(); auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration); if (!base) reportFatalTypeError(baseName.createTypeError("Contract expected.")); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 84e9e706..604a12a0 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -340,7 +340,7 @@ class InheritanceSpecifier: public ASTNode public: InheritanceSpecifier( SourceLocation const& _location, - ASTPointer<Identifier> const& _baseName, + ASTPointer<UserDefinedTypeName> const& _baseName, std::vector<ASTPointer<Expression>> _arguments ): ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {} @@ -348,11 +348,11 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Identifier const& name() const { return *m_baseName; } + UserDefinedTypeName const& name() const { return *m_baseName; } std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; } private: - ASTPointer<Identifier> m_baseName; + ASTPointer<UserDefinedTypeName> m_baseName; std::vector<ASTPointer<Expression>> m_arguments; }; @@ -366,7 +366,7 @@ class UsingForDirective: public ASTNode public: UsingForDirective( SourceLocation const& _location, - ASTPointer<Identifier> const& _libraryName, + ASTPointer<UserDefinedTypeName> const& _libraryName, ASTPointer<TypeName> const& _typeName ): ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {} @@ -374,12 +374,12 @@ public: virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; - Identifier const& libraryName() const { return *m_libraryName; } + UserDefinedTypeName const& libraryName() const { return *m_libraryName; } /// @returns the type name the library is attached to, null for `*`. TypeName const* typeName() const { return m_typeName.get(); } private: - ASTPointer<Identifier> m_libraryName; + ASTPointer<UserDefinedTypeName> m_libraryName; ASTPointer<TypeName> m_typeName; }; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index b2ce1c26..d4f13e47 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -63,7 +63,7 @@ bool ASTPrinter::visit(ContractDefinition const& _node) bool ASTPrinter::visit(InheritanceSpecifier const& _node) { - writeLine("InheritanceSpecifier \"" + _node.name().name() + "\""); + writeLine("InheritanceSpecifier"); printSourcePart(_node); return goDeeper(); } diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index d53f825f..7dd3564d 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -238,7 +238,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary) ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier() { ASTNodeFactory nodeFactory(*this); - ASTPointer<Identifier> name(parseIdentifier()); + ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName()); vector<ASTPointer<Expression>> arguments; if (m_scanner->currentToken() == Token::LParen) { @@ -533,8 +533,7 @@ ASTPointer<UsingForDirective> Parser::parseUsingDirective() ASTNodeFactory nodeFactory(*this); expectToken(Token::Using); - //@todo this should actually parse a full path. - ASTPointer<Identifier> library(parseIdentifier()); + ASTPointer<UserDefinedTypeName> library(parseUserDefinedTypeName()); ASTPointer<TypeName> typeName; expectToken(Token::For); if (m_scanner->currentToken() == Token::Mul) @@ -570,6 +569,20 @@ ASTPointer<Identifier> Parser::parseIdentifier() return nodeFactory.createNode<Identifier>(expectIdentifierToken()); } +ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName() +{ + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); + vector<ASTString> identifierPath{*expectIdentifierToken()}; + while (m_scanner->currentToken() == Token::Period) + { + m_scanner->next(); + nodeFactory.markEndPosition(); + identifierPath.push_back(*expectIdentifierToken()); + } + return nodeFactory.createNode<UserDefinedTypeName>(identifierPath); +} + ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) { ASTNodeFactory nodeFactory(*this); @@ -589,18 +602,7 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) else if (token == Token::Mapping) type = parseMapping(); else if (token == Token::Identifier) - { - ASTNodeFactory nodeFactory(*this); - nodeFactory.markEndPosition(); - vector<ASTString> identifierPath{*expectIdentifierToken()}; - while (m_scanner->currentToken() == Token::Period) - { - m_scanner->next(); - nodeFactory.markEndPosition(); - identifierPath.push_back(*expectIdentifierToken()); - } - type = nodeFactory.createNode<UserDefinedTypeName>(identifierPath); - } + type = parseUserDefinedTypeName(); else fatalParserError(string("Expected type name")); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 5ff46242..eb1aa587 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -77,6 +77,7 @@ private: ASTPointer<UsingForDirective> parseUsingDirective(); ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<Identifier> parseIdentifier(); + ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName(); ASTPointer<TypeName> parseTypeName(bool _allowVar); ASTPointer<Mapping> parseMapping(); ASTPointer<ParameterList> parseParameterList( diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 57239cb9..f0f67785 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(simple_alias) { CompilerStack c; c.addSource("a", "contract A {}"); - c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B { function() { x.A r = x.A(20); } }"); + c.addSource("dir/a/b/c", "import \"../../.././a\" as x; contract B is x.A { function() { x.A r = x.A(20); } }"); BOOST_CHECK(c.compile()); } |