diff options
author | Chris Ward <chris.ward@ethereum.org> | 2019-01-08 01:05:27 +0800 |
---|---|---|
committer | Chris Ward <chris.ward@ethereum.org> | 2019-01-08 01:13:32 +0800 |
commit | 13cd96136aa499303a4c2ed92848366ebd0b9343 (patch) | |
tree | 89b7bbcd9c4d1984abbef0511d73c0dedc4f0a32 /docs/contracts.rst | |
parent | a2926cd9dcdddf681b87471cef8ed0c83c3aefd3 (diff) | |
download | dexon-solidity-13cd96136aa499303a4c2ed92848366ebd0b9343.tar.gz dexon-solidity-13cd96136aa499303a4c2ed92848366ebd0b9343.tar.zst dexon-solidity-13cd96136aa499303a4c2ed92848366ebd0b9343.zip |
Split libraries into new doc
Diffstat (limited to 'docs/contracts.rst')
-rw-r--r-- | docs/contracts.rst | 231 |
1 files changed, 1 insertions, 230 deletions
diff --git a/docs/contracts.rst b/docs/contracts.rst index 3c91b2f8..dc1e71e9 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -1073,236 +1073,7 @@ Contracts can inherit interfaces as they would inherit other contracts. Types defined inside interfaces and other contract-like structures can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``. -.. index:: ! library, callcode, delegatecall - -.. _libraries: - -********* -Libraries -********* - -Libraries are similar to contracts, but their purpose is that they are deployed -only once at a specific address and their code is reused using the ``DELEGATECALL`` -(``CALLCODE`` until Homestead) -feature of the EVM. This means that if library functions are called, their code -is executed in the context of the calling contract, i.e. ``this`` points to the -calling contract, and especially the storage from the calling contract can be -accessed. As a library is an isolated piece of source code, it can only access -state variables of the calling contract if they are explicitly supplied (it -would have no way to name them, otherwise). Library functions can only be -called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify -the state (i.e. if they are ``view`` or ``pure`` functions), -because libraries are assumed to be stateless. In particular, it is -not possible to destroy a library. - -.. note:: - Until version 0.4.20, it was possible to destroy libraries by - circumventing Solidity's type system. Starting from that version, - libraries contain a :ref:`mechanism<call-protection>` that - disallows state-modifying functions - to be called directly (i.e. without ``DELEGATECALL``). - -Libraries can be seen as implicit base contracts of the contracts that use them. -They will not be explicitly visible in the inheritance hierarchy, but calls -to library functions look just like calls to functions of explicit base -contracts (``L.f()`` if ``L`` is the name of the library). Furthermore, -``internal`` functions of libraries are visible in all contracts, just as -if the library were a base contract. Of course, calls to internal functions -use the internal calling convention, which means that all internal types -can be passed and types :ref:`stored in memory <data-location>` will be passed by reference and not copied. -To realize this in the EVM, code of internal library functions -and all functions called from therein will at compile time be pulled into the calling -contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``. - -.. index:: using for, set - -The following example illustrates how to use libraries (but manual method -be sure to check out :ref:`using for <using-for>` for a -more advanced example to implement a set). - -:: - - pragma solidity >=0.4.22 <0.6.0; - - library Set { - // We define a new struct datatype that will be used to - // hold its data in the calling contract. - struct Data { mapping(uint => bool) flags; } - - // Note that the first parameter is of type "storage - // reference" and thus only its storage address and not - // its contents is passed as part of the call. This is a - // special feature of library functions. It is idiomatic - // to call the first parameter `self`, if the function can - // be seen as a method of that object. - function insert(Data storage self, uint value) - public - returns (bool) - { - if (self.flags[value]) - return false; // already there - self.flags[value] = true; - return true; - } - - function remove(Data storage self, uint value) - public - returns (bool) - { - if (!self.flags[value]) - return false; // not there - self.flags[value] = false; - return true; - } - - function contains(Data storage self, uint value) - public - view - returns (bool) - { - return self.flags[value]; - } - } - - contract C { - Set.Data knownValues; - - function register(uint value) public { - // The library functions can be called without a - // specific instance of the library, since the - // "instance" will be the current contract. - require(Set.insert(knownValues, value)); - } - // 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 -libraries: they can also be used without defining struct -data types. Functions also work without any storage -reference parameters, and they can have multiple storage reference -parameters and in any position. - -The calls to ``Set.contains``, ``Set.insert`` and ``Set.remove`` -are all compiled as calls (``DELEGATECALL``) to an external -contract/library. If you use libraries, be aware that an -actual external function call is performed. -``msg.sender``, ``msg.value`` and ``this`` will retain their values -in this call, though (prior to Homestead, because of the use of ``CALLCODE``, ``msg.sender`` and -``msg.value`` changed, though). - -The following example shows how to use :ref:`types stored in memory <data-location>` and -internal functions in libraries in order to implement -custom types without the overhead of external function calls: - -:: - - pragma solidity >=0.4.16 <0.6.0; - - library BigInt { - struct bigint { - uint[] limbs; - } - - function fromUint(uint x) internal pure returns (bigint memory r) { - r.limbs = new uint[](1); - r.limbs[0] = x; - } - - function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) { - r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length)); - uint carry = 0; - for (uint i = 0; i < r.limbs.length; ++i) { - uint a = limb(_a, i); - uint b = limb(_b, i); - r.limbs[i] = a + b + carry; - if (a + b < a || (a + b == uint(-1) && carry > 0)) - carry = 1; - else - carry = 0; - } - if (carry > 0) { - // too bad, we have to add a limb - uint[] memory newLimbs = new uint[](r.limbs.length + 1); - uint i; - for (i = 0; i < r.limbs.length; ++i) - newLimbs[i] = r.limbs[i]; - newLimbs[i] = carry; - r.limbs = newLimbs; - } - } - - function limb(bigint memory _a, uint _limb) internal pure returns (uint) { - return _limb < _a.limbs.length ? _a.limbs[_limb] : 0; - } - - function max(uint a, uint b) private pure returns (uint) { - return a > b ? a : b; - } - } - - contract C { - using BigInt for BigInt.bigint; - - function f() public pure { - BigInt.bigint memory x = BigInt.fromUint(7); - BigInt.bigint memory y = BigInt.fromUint(uint(-1)); - BigInt.bigint memory z = x.add(y); - assert(z.limb(1) > 0); - } - } - -As the compiler cannot know where the library will be -deployed at, these addresses have to be filled into the -final bytecode by a linker -(see :ref:`commandline-compiler` for how to use the -commandline compiler for linking). If the addresses are not -given as arguments to the compiler, the compiled hex code -will contain placeholders of the form ``__Set______`` (where -``Set`` is the name of the library). The address can be filled -manually by replacing all those 40 symbols by the hex -encoding of the address of the library contract. - -.. note:: - Manually linking libraries on the generated bytecode is discouraged, because - it is restricted to 36 characters. - You should ask the compiler to link the libraries at the time - a contract is compiled by either using - the ``--libraries`` option of ``solc`` or the ``libraries`` key if you use - the standard-JSON interface to the compiler. - -Restrictions for libraries in comparison to contracts: - -- No state variables -- Cannot inherit nor be inherited -- Cannot receive Ether - -(These might be lifted at a later point.) - -.. _call-protection: - -Call Protection For Libraries -============================= - -As mentioned in the introduction, if a library's code is executed -using a ``CALL`` instead of a ``DELEGATECALL`` or ``CALLCODE``, -it will revert unless a ``view`` or ``pure`` function is called. - -The EVM does not provide a direct way for a contract to detect -whether it was called using ``CALL`` or not, but a contract -can use the ``ADDRESS`` opcode to find out "where" it is -currently running. The generated code compares this address -to the address used at construction time to determine the mode -of calling. - -More specifically, the runtime code of a library always starts -with a push instruction, which is a zero of 20 bytes at -compilation time. When the deploy code runs, this constant -is replaced in memory by the current address and this -modified code is stored in the contract. At runtime, -this causes the deploy time address to be the first -constant to be pushed onto the stack and the dispatcher -code compares the current address against this constant -for any non-view and non-pure function. +.. include:: contracts/libraries.rst .. index:: ! using for, library |