Merge branch 'develop' into anurag_issue_3667

This commit is contained in:
Anurag Dashputre 2018-08-23 11:56:45 +05:30 committed by GitHub
commit 8497dcd721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
217 changed files with 3814 additions and 924 deletions

View File

@ -179,6 +179,17 @@ jobs:
name: Check spelling
command: ~/.local/bin/codespell -S "*.enc,.git" -I ./scripts/codespell_whitelist.txt
test_buglist:
docker:
- image: circleci/node
environment:
TERM: xterm
steps:
- checkout
- run:
name: Test buglist
command: ./test/buglistTests.js
test_x86_linux:
docker:
- image: buildpack-deps:artful
@ -252,6 +263,7 @@ workflows:
build_all:
jobs:
- test_check_spelling: *build_on_tags
- test_buglist: *build_on_tags
- build_emscripten: *build_on_tags
- test_emscripten_solcjs:
<<: *build_on_tags

View File

@ -5,7 +5,7 @@ How to update your code:
* Change every ``keccak256(a, b, c)`` to ``keccak256(abi.encodePacked(a, b, c))``.
* Add ``public`` to every function and ``external`` to every fallback or interface function that does not specify its visibility already.
* Make your fallback functions ``external``.
* Explicitly state the storage location for local variables of struct and array types, e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``.
* Explicitly state the data location for all variables of struct, array or mapping types (including function parameters), e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``. Note that ``external`` functions require parameters with a data location of ``calldata``.
* Explicitly convert values of contract type to addresses before using an ``address`` member. Example: if ``c`` is a contract, change ``c.transfer(...)`` to ``address(c).transfer(...)``.
Breaking Changes:
@ -16,6 +16,8 @@ Breaking Changes:
* Commandline interface: Remove obsolete ``--formal`` option.
* Commandline interface: Rename the ``--julia`` option to ``--yul``.
* Commandline interface: Require ``-`` if standard input is used as source.
* Compiler interface: Disallow remappings with empty prefix.
* Control Flow Analyzer: Consider mappings as well when checking for uninitialized return values.
* Control Flow Analyzer: Turn warning about returning uninitialized storage pointers into an error.
* General: ``continue`` in a ``do...while`` loop jumps to the condition (it used to jump to the loop body). Warning: this may silently change the semantics of existing code.
* General: Disallow declaring empty structs.
@ -52,11 +54,14 @@ Breaking Changes:
* Type Checker: Disallow calling constructor with wrong argument count. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Disallow uninitialized storage variables. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Detecting cyclic dependencies in variables and structs is limited in recursion to 256.
* Type Checker: Require explicit data location for all variables, including function parameters. This was partly already the case in the experimental 0.5.0 mode.
* Type Checker: Only accept a single ``bytes`` type for ``.call()`` (and family), ``keccak256()``, ``sha256()`` and ``ripemd160()``.
* Type Checker: Fallback function must be external. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Interface functions must be declared external. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Address members are not included in contract types anymore. An explicit conversion is now required before invoking an ``address`` member from a contract.
* Type Checker: Disallow "loose assembly" syntax entirely. This means that jump labels, jumps and non-functional instructions cannot be used anymore.
* Type System: Disallow explicit and implicit conversions from decimal literals to ``bytesXX`` types.
* Type System: Disallow explicit and implicit conversions from hex literals to ``bytesXX`` types of different size.
* Remove obsolete ``std`` directory from the Solidity repository. This means accessing ``https://github.com/ethereum/solidity/blob/develop/std/*.sol`` (or ``https://github.com/ethereum/solidity/std/*.sol`` in Remix) will not be possible.
* References Resolver: Turn missing storage locations into an error. This was already the case in the experimental 0.5.0 mode.
* Syntax Checker: Disallow functions without implementation to use modifiers. This was already the case in the experimental 0.5.0 mode.
@ -66,10 +71,14 @@ Breaking Changes:
* View Pure Checker: Strictly enfore state mutability. This was already the case in the experimental 0.5.0 mode.
Language Features:
* Genreal: Add ``staticcall`` to ``address``.
* General: Allow appending ``calldata`` keyword to types, to explicitly specify data location for arguments of external functions.
* General: Support ``pop()`` for storage arrays.
* General: Scoping rules now follow the C99-style.
* General: Allow ``enum``s in interfaces.
* General: Allow ``mapping`` storage pointers as arguments and return values in all internal functions.
* General: Allow ``struct``s in interfaces.
* General: Provide access to the ABI decoder through ``abi.decode(bytes memory data, (...))``.
Compiler Features:
* C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods.
@ -86,15 +95,22 @@ Bugfixes:
* Code Generator: Properly handle negative number literals in ABIEncoderV2.
* Commandline Interface: Correctly handle paths with backslashes on windows.
* Fix NatSpec json output for `@notice` and `@dev` tags on contract definitions.
* Optimizer: Correctly estimate gas costs of constants for special cases.
* References Resolver: Do not crash on using ``_slot`` and ``_offset`` suffixes on their own.
* References Resolver: Enforce ``storage`` as data location for mappings.
* References Resolver: Properly handle invalid references used together with ``_slot`` and ``_offset``.
* References Resolver: Report error instead of assertion fail when FunctionType has an undeclared type as parameter.
* References Resolver: Fix high CPU usage when using large variable names issue. Only suggest similar name if identifiers shorter than 80 characters.
* Type Checker: Default data location for type conversions (e.g. from literals) is memory and not storage.
* Type Checker: Disallow assignments to mappings within tuple assignments as well.
* Type Checker: Disallow packed encoding of arrays of structs.
* Type Checker: Allow assignments to local variables of mapping types.
* Type Checker: Consider fixed size arrays when checking for recursive structs.
* Type Checker: Fix crashes in erroneous tuple assignments in which the type of the right hand side cannot be determined.
* Type Checker: Fix freeze for negative fixed-point literals very close to ``0``, such as ``-1e-100``.
* Type Checker: Report error when using structs in events without experimental ABIEncoderV2. This used to crash or log the wrong values.
* Type Checker: Report error when using indexed structs in events with experimental ABIEncoderV2. This used to log wrong values.
* Type Checker: Dynamic types as key for public mappings return error instead of assertion fail.
* Type System: Allow arbitrary exponents for literals with a mantissa of zero.
### 0.4.24 (2018-05-16)

View File

@ -7,8 +7,8 @@ pre {
}
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: pre-wrap;
white-space: normal;
}
.wy-table-responsive table td, .wy-table-responsive table th {
white-space: pre-wrap;
.rst-content table.docutils td {
vertical-align: top;
}

View File

@ -151,8 +151,8 @@ on the type of ``X`` being
- ``bytes``, of length ``k`` (which is assumed to be of type ``uint256``):
``enc(X) = enc(k) pad_right(X)``, i.e. the number of bytes is encoded as a
``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by
the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32.
``uint256`` followed by the actual value of ``X`` as a byte sequence, followed by
the minimum number of zero-bytes such that ``len(enc(X))`` is a multiple of 32.
- ``string``:
@ -205,7 +205,9 @@ Thus for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``
- ``0x0000000000000000000000000000000000000000000000000000000000000045``: the first parameter, a uint32 value ``69`` padded to 32 bytes
- ``0x0000000000000000000000000000000000000000000000000000000000000001``: the second parameter - boolean ``true``, padded to 32 bytes
In total::
In total:
.. code-block:: none
0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001
@ -217,7 +219,9 @@ If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass
- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first parameter, a ``bytes3`` value ``"abc"`` (left-aligned).
- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first parameter, a ``bytes3`` value ``"def"`` (left-aligned).
In total::
In total:
.. code-block:: none
0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000
@ -234,7 +238,9 @@ If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2,
- ``0x0000000000000000000000000000000000000000000000000000000000000002``: the second entry of the third parameter.
- ``0x0000000000000000000000000000000000000000000000000000000000000003``: the third entry of the third parameter.
In total::
In total:
.. code-block:: none
0xa5643bf20000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000464617665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003
@ -264,7 +270,7 @@ Finally, we encode the data part of the second dynamic argument, ``"Hello, world
All together, the encoding is (newline after function selector and each 32-bytes for clarity):
::
.. code-block:: none
0x8be65246
0000000000000000000000000000000000000000000000000000000000000123
@ -292,15 +298,15 @@ Then we encode the length and data of the second embedded dynamic array ``[3]``
Then we need to find the offsets ``a`` and ``b`` for their respective dynamic arrays ``[1, 2]`` and ``[3]``. To calculate the offsets we can take a look at the encoded data of the first root array ``[[1, 2], [3]]`` enumerating each line in the encoding:
::
.. code-block:: none
0 - a - offset of [1, 2]
1 - b - offset of [3]
2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2]
3 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1
4 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2
5 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3]
6 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3
0 - a - offset of [1, 2]
1 - b - offset of [3]
2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2]
3 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1
4 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2
5 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3]
6 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3
Offset ``a`` points to the start of the content of the array ``[1, 2]`` which is line 2 (64 bytes); thus ``a = 0x0000000000000000000000000000000000000000000000000000000000000040``.
@ -318,17 +324,17 @@ Then we encode the embedded strings of the second root array:
In parallel to the first root array, since strings are dynamic elements we need to find their offsets ``c``, ``d`` and ``e``:
::
.. code-block:: none
0 - c - offset for "one"
1 - d - offset for "two"
2 - e - offset for "three"
3 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one"
4 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one"
5 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two"
6 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two"
7 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three"
8 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three"
0 - c - offset for "one"
1 - d - offset for "two"
2 - e - offset for "three"
3 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one"
4 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one"
5 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two"
6 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two"
7 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three"
8 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three"
Offset ``c`` points to the start of the content of the string ``"one"`` which is line 3 (96 bytes); thus ``c = 0x0000000000000000000000000000000000000000000000000000000000000060``.
@ -349,29 +355,29 @@ Then we encode the length of the second root array:
Finally we find the offsets ``f`` and ``g`` for their respective root dynamic arrays ``[[1, 2], [3]]`` and ``["one", "two", "three"]``, and assemble parts in the correct order:
::
.. code-block:: none
0x2289b18c - function signature
0 - f - offset of [[1, 2], [3]]
1 - g - offset of ["one", "two", "three"]
2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [[1, 2], [3]]
3 - 0000000000000000000000000000000000000000000000000000000000000040 - offset of [1, 2]
4 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset of [3]
5 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2]
6 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1
7 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2
8 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3]
9 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3
10 - 0000000000000000000000000000000000000000000000000000000000000003 - count for ["one", "two", "three"]
11 - 0000000000000000000000000000000000000000000000000000000000000060 - offset for "one"
12 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset for "two"
13 - 00000000000000000000000000000000000000000000000000000000000000e0 - offset for "three"
14 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one"
15 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one"
16 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two"
17 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two"
18 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three"
19 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three"
0x2289b18c - function signature
0 - f - offset of [[1, 2], [3]]
1 - g - offset of ["one", "two", "three"]
2 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [[1, 2], [3]]
3 - 0000000000000000000000000000000000000000000000000000000000000040 - offset of [1, 2]
4 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset of [3]
5 - 0000000000000000000000000000000000000000000000000000000000000002 - count for [1, 2]
6 - 0000000000000000000000000000000000000000000000000000000000000001 - encoding of 1
7 - 0000000000000000000000000000000000000000000000000000000000000002 - encoding of 2
8 - 0000000000000000000000000000000000000000000000000000000000000001 - count for [3]
9 - 0000000000000000000000000000000000000000000000000000000000000003 - encoding of 3
10 - 0000000000000000000000000000000000000000000000000000000000000003 - count for ["one", "two", "three"]
11 - 0000000000000000000000000000000000000000000000000000000000000060 - offset for "one"
12 - 00000000000000000000000000000000000000000000000000000000000000a0 - offset for "two"
13 - 00000000000000000000000000000000000000000000000000000000000000e0 - offset for "three"
14 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "one"
15 - 6f6e650000000000000000000000000000000000000000000000000000000000 - encoding of "one"
16 - 0000000000000000000000000000000000000000000000000000000000000003 - count for "two"
17 - 74776f0000000000000000000000000000000000000000000000000000000000 - encoding of "two"
18 - 0000000000000000000000000000000000000000000000000000000000000005 - count for "three"
19 - 7468726565000000000000000000000000000000000000000000000000000000 - encoding of "three"
Offset ``f`` points to the start of the content of the array ``[[1, 2], [3]]`` which is line 2 (64 bytes); thus ``f = 0x0000000000000000000000000000000000000000000000000000000000000040``.
@ -444,7 +450,7 @@ For example,
pragma solidity >0.4.24;
contract Test {
constructor() public { b = 0x12345678901234567890123456789012; }
constructor() public { b = hex"12345678901234567890123456789012"; }
event Event(uint indexed a, bytes32 b);
event Event2(uint indexed a, bytes32 b);
function foo(uint a) public { emit Event(a, b); }
@ -494,8 +500,8 @@ As an example, the code
contract Test {
struct S { uint a; uint[] b; T[] c; }
struct T { uint x; uint y; }
function f(S memory s, T memory t, uint a) public { }
function g() public returns (S memory s, T memory t, uint a) {}
function f(S memory s, T memory t, uint a) public;
function g() public returns (S memory s, T memory t, uint a);
}
would result in the JSON:
@ -568,7 +574,9 @@ Through ``abi.encodePacked()``, Solidity supports a non-standard packed mode whe
- types shorter than 32 bytes are neither zero padded nor sign extended and
- dynamic types are encoded in-place and without the length.
As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in ::
As an example encoding ``int8, bytes1, uint16, string`` with values ``-1, 0x42, 0x2424, "Hello, world!"`` results in:
.. code-block:: none
0xff42242448656c6c6f2c20776f726c6421
^^ int8(-1)

View File

@ -82,7 +82,7 @@ you really know what you are doing.
library VectorSum {
// This function is less efficient because the optimizer currently fails to
// remove the bounds checks in array access.
function sumSolidity(uint[] memory _data) public view returns (uint o_sum) {
function sumSolidity(uint[] memory _data) public pure returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i)
o_sum += _data[i];
}
@ -90,7 +90,7 @@ you really know what you are doing.
// We know that we only access the array in bounds, so we can avoid the check.
// 0x20 needs to be added to an array because the first slot contains the
// array length.
function sumAsm(uint[] memory _data) public view returns (uint o_sum) {
function sumAsm(uint[] memory _data) public pure returns (uint o_sum) {
for (uint i = 0; i < _data.length; ++i) {
assembly {
o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
@ -99,7 +99,7 @@ you really know what you are doing.
}
// Same as above, but accomplish the entire code within inline assembly.
function sumPureAsm(uint[] memory _data) public view returns (uint o_sum) {
function sumPureAsm(uint[] memory _data) public pure returns (uint o_sum) {
assembly {
// Load the length (first 32 bytes)
let len := mload(_data)
@ -378,23 +378,13 @@ used ``x_slot`` and to retrieve the byte-offset you used ``x_offset``.
In assignments (see below), we can even use local Solidity variables to assign to.
Functions external to inline assembly can also be accessed: The assembly will
push their entry label (with virtual function resolution applied). The calling semantics
in solidity are:
- the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn``
- the call returns with ``ret1``, ``ret2``, ..., ``retm``
This feature is still a bit cumbersome to use, because the stack offset essentially
changes during the call, and thus references to local variables will be wrong.
.. code::
pragma solidity ^0.4.11;
contract C {
uint b;
function f(uint x) public returns (uint r) {
function f(uint x) public view returns (uint r) {
assembly {
r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero
}

View File

@ -1,4 +1,21 @@
[
{
"name": "EventStructWrongData",
"summary": "Using structs in events logged wrong data.",
"description": "If a struct is used in an event, the address of the struct is logged instead of the actual data.",
"introduced": "0.4.17",
"fixed": "0.5.0",
"severity": "very low"
},
{
"name": "NestedArrayFunctionCallDecoder",
"summary": "Calling functions that return multi-dimensional fixed-size arrays can result in memory corruption.",
"description": "If Solidity code calls a function that returns a multi-dimensional fixed-size array, array elements are incorrectly interpreted as memory pointers and thus can cause memory corruption if the return values are accessed. Calling functions with multi-dimensional fixed-size arrays is unaffected as is returning fixed-size arrays from function calls. The regular expression only checks if such functions are present, not if they are called, which is required for the contract to be affected.",
"introduced": "0.1.4",
"fixed": "0.4.22",
"severity": "medium",
"check": {"regex-source": "returns[^;{]*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\]\\s*\\[\\s*[^\\] \\t\\r\\n\\v\\f][^\\]]*\\][^{;]*[;{]"}
},
{
"name": "OneOfTwoConstructorsSkipped",
"summary": "If a contract has both a new-style constructor (using the constructor keyword) and an old-style constructor (a function with the same name as the contract) at the same time, one of them will be ignored.",

View File

@ -56,6 +56,14 @@ conditions
is an object that can contain a boolean value ``optimizer``, which
means that the optimizer has to be switched on to enable the bug.
If no conditions are given, assume that the bug is present.
check
This field contains JavaScript regular expressions that are to be matched
against the source code ("source-regex") to find out if the
smart contract contains the bug or not. If there is no match,
then the bug is very likely not present. If there is a match,
the bug might be present. For improved accuracy, the regular
expression should be applied to the source code after stripping
comments.
.. literalinclude:: bugs.json
:language: js

View File

@ -69,6 +69,7 @@
},
"0.1.4": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -86,6 +87,7 @@
},
"0.1.5": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -103,6 +105,7 @@
},
"0.1.6": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -121,6 +124,7 @@
},
"0.1.7": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -139,6 +143,7 @@
},
"0.2.0": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -157,6 +162,7 @@
},
"0.2.1": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -175,6 +181,7 @@
},
"0.2.2": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"ECRecoverMalformedInput",
"SkipEmptyStringLiteral",
@ -193,6 +200,7 @@
},
"0.3.0": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -211,6 +219,7 @@
},
"0.3.1": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -228,6 +237,7 @@
},
"0.3.2": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -245,6 +255,7 @@
},
"0.3.3": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -261,6 +272,7 @@
},
"0.3.4": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -277,6 +289,7 @@
},
"0.3.5": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -293,6 +306,7 @@
},
"0.3.6": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -307,6 +321,7 @@
},
"0.4.0": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -321,6 +336,7 @@
},
"0.4.1": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -335,6 +351,7 @@
},
"0.4.10": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -345,6 +362,7 @@
},
"0.4.11": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -354,6 +372,7 @@
},
"0.4.12": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput"
@ -362,6 +381,7 @@
},
"0.4.13": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput"
@ -370,6 +390,7 @@
},
"0.4.14": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue"
],
@ -377,32 +398,43 @@
},
"0.4.15": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-08-08"
},
"0.4.16": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-08-24"
},
"0.4.17": {
"bugs": [
"EventStructWrongData",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector"
],
"released": "2017-09-21"
},
"0.4.18": {
"bugs": [],
"bugs": [
"EventStructWrongData",
"NestedArrayFunctionCallDecoder"
],
"released": "2017-10-18"
},
"0.4.19": {
"bugs": [],
"bugs": [
"EventStructWrongData",
"NestedArrayFunctionCallDecoder"
],
"released": "2017-11-30"
},
"0.4.2": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -415,29 +447,41 @@
"released": "2016-09-17"
},
"0.4.20": {
"bugs": [],
"bugs": [
"EventStructWrongData",
"NestedArrayFunctionCallDecoder"
],
"released": "2018-02-14"
},
"0.4.21": {
"bugs": [],
"bugs": [
"EventStructWrongData",
"NestedArrayFunctionCallDecoder"
],
"released": "2018-03-07"
},
"0.4.22": {
"bugs": [
"EventStructWrongData",
"OneOfTwoConstructorsSkipped"
],
"released": "2018-04-16"
},
"0.4.23": {
"bugs": [],
"bugs": [
"EventStructWrongData"
],
"released": "2018-04-19"
},
"0.4.24": {
"bugs": [],
"bugs": [
"EventStructWrongData"
],
"released": "2018-05-16"
},
"0.4.3": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -450,6 +494,7 @@
},
"0.4.4": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -461,6 +506,7 @@
},
"0.4.5": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -473,6 +519,7 @@
},
"0.4.6": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -484,6 +531,7 @@
},
"0.4.7": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -494,6 +542,7 @@
},
"0.4.8": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",
@ -504,6 +553,7 @@
},
"0.4.9": {
"bugs": [
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
"DelegateCallReturnValue",
"ECRecoverMalformedInput",

View File

@ -198,7 +198,7 @@ restrictions highly readable.
);
_;
if (msg.value > _amount)
msg.sender.send(msg.value - _amount);
msg.sender.transfer(msg.value - _amount);
}
function forceOwnerChange(address _newOwner)

View File

@ -143,7 +143,7 @@ html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
html_static_path = ['_static']
# Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied

View File

@ -26,8 +26,8 @@ Creating contracts programmatically on Ethereum is best done via using the JavaS
As of today it has a method called `web3.eth.Contract <https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html#new-contract>`_
to facilitate contract creation.
When a contract is created, its constructor (a function declared with the
``constructor`` keyword) is executed once.
When a contract is created, its constructor_ (a function declared with the ``constructor`` keyword) is executed once.
A constructor is optional. Only one constructor is allowed, and this means
overloading is not supported.
@ -110,12 +110,11 @@ This means that cyclic creation dependencies are impossible.
function isTokenTransferOK(address currentOwner, address newOwner)
public
view
pure
returns (bool ok)
{
// Check some arbitrary condition.
address tokenAddress = msg.sender;
return (keccak256(abi.encodePacked(newOwner)) & 0xff) == (bytes20(tokenAddress) & 0xff);
return keccak256(abi.encodePacked(currentOwner, newOwner))[0] == 0x7f;
}
}
@ -138,16 +137,16 @@ Functions have to be specified as being ``external``,
For state variables, ``external`` is not possible.
``external``:
External functions are part of the contract
interface, which means they can be called from other contracts and
External functions are part of the contract interface,
which means they can be called from other contracts and
via transactions. An external function ``f`` cannot be called
internally (i.e. ``f()`` does not work, but ``this.f()`` works).
External functions are sometimes more efficient when
they receive large arrays of data.
``public``:
Public functions are part of the contract
interface and can be either called internally or via
Public functions are part of the contract interface
and can be either called internally or via
messages. For public state variables, an automatic getter
function (see below) is generated.
@ -188,8 +187,6 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
::
// This will not compile
pragma solidity ^0.4.0;
contract C {
@ -201,6 +198,7 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
// This will not compile
contract D {
function readData() public {
C c = new C();
@ -228,8 +226,8 @@ The compiler automatically creates getter functions for
all **public** state variables. For the contract given below, the compiler will
generate a function called ``data`` that does not take any
arguments and returns a ``uint``, the value of the state
variable ``data``. The initialization of state variables can
be done at declaration.
variable ``data``. State variables can be initialized
when they are declared.
::
@ -241,8 +239,8 @@ be done at declaration.
contract Caller {
C c = new C();
function f() public {
uint local = c.data();
function f() public view returns (uint) {
return c.data();
}
}
@ -257,13 +255,43 @@ it is evaluated as a state variable. If it is accessed externally
contract C {
uint public data;
function x() public {
function x() public returns (uint) {
data = 3; // internal access
uint val = this.data(); // external access
return this.data(); // external access
}
}
The next example is a bit more complex:
If you have a `public` state variable of array type, then you can only retrieve
single elements of the array via the generated getter function. This mechanism
exists to avoid high gas costs when returning an entire array. You can use
arguments to specify which individual element to return, for example
``data(0)``. If you want to return an entire array in one call, then you need
to write a function, for example:
::
pragma solidity ^0.4.0;
contract arrayExample {
// public state variable
uint[] public myArray;
// Getter function generated by the compiler
/*
function myArray(uint i) returns (uint) {
return myArray[i];
}
*/
// function that returns entire array
function getArray() returns (uint[] memory) {
return myArray;
}
}
Now you can use ``getArray()`` to retrieve the entire array, instead of
``myArray(i)``, which returns a single element per call.
The next example is more complex:
::
@ -278,16 +306,16 @@ The next example is a bit more complex:
mapping (uint => mapping(bool => Data[])) public data;
}
It will generate a function of the following form::
It generates a function of the following form. The mapping in the struct is omitted
because there is no good way to provide the key for the mapping:
::
function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) {
a = data[arg1][arg2][arg3].a;
b = data[arg1][arg2][arg3].b;
}
Note that the mapping in the struct is omitted because there
is no good way to provide the key for the mapping.
.. index:: ! function;modifier
.. _modifiers:
@ -616,14 +644,13 @@ Like any function, the fallback function can execute complex operations as long
}
contract Caller {
function callTest(Test test) public {
address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
function callTest(Test test) public returns (bool) {
require(address(test).call(abi.encodeWithSignature("nonExistingFunction()")));
// results in test.x becoming == 1.
// If someone sends ether to that contract,
// the transaction will fail and reject the
// Ether.
address(test).send(2 ether);
// the transfer will fail, i.e. this returns false here.
return address(test).send(2 ether);
}
}
@ -634,9 +661,11 @@ Like any function, the fallback function can execute complex operations as long
Function Overloading
====================
A Contract can have multiple functions of the same name but with different arguments.
This also applies to inherited functions. The following example shows overloading of the
``f`` function in the scope of contract ``A``.
A contract can have multiple functions of the same name but with different parameter
types.
This process is called "overloading" and also applies to inherited functions.
The following example shows overloading of the function
``f`` in the scope of contract ``A``.
::
@ -644,11 +673,12 @@ This also applies to inherited functions. The following example shows overloadin
contract A {
function f(uint _in) public pure returns (uint out) {
out = 1;
out = _in;
}
function f(uint _in, bytes32 _key) public pure returns (uint out) {
out = 2;
function f(uint _in, bool _really) public pure returns (uint out) {
if (_really)
out = _in;
}
}
@ -657,9 +687,9 @@ externally visible functions differ by their Solidity types but not by their ext
::
// This will not compile
pragma solidity ^0.4.16;
// This will not compile
contract A {
function f(B _in) public pure returns (B out) {
out = _in;
@ -811,12 +841,12 @@ as topics. The event call above can be performed in the same way as
contract C {
function f() public payable {
bytes32 _id = 0x420042;
uint256 _id = 0x420042;
log3(
bytes32(msg.value),
bytes32(0x50cb9fe53daa9737b786ab3646f04d0150dc50ef4e75f59509d83667ad5adb20),
bytes32(uint256(msg.sender)),
_id
bytes32(_id)
);
}
}
@ -842,13 +872,14 @@ Solidity supports multiple inheritance by copying code including polymorphism.
All function calls are virtual, which means that the most derived function
is called, except when the contract name is explicitly given.
When a contract inherits from multiple contracts, only a single
When a contract inherits from other contracts, only a single
contract is created on the blockchain, and the code from all the base contracts
is copied into the created contract.
The general inheritance system is very similar to
`Python's <https://docs.python.org/3/tutorial/classes.html#inheritance>`_,
especially concerning multiple inheritance.
especially concerning multiple inheritance, but there are also
some :ref:`differences <multi-inheritance>`.
Details are given in the following example.
@ -993,12 +1024,26 @@ virtual method lookup.
.. index:: ! constructor
.. _constructor:
Constructors
============
A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the
default constructor: ``contructor() public {}``.
A constructor is an optional function declared with the ``constructor`` keyword
which is executed upon contract creation, and where you can run contract
initialisation code.
Before the constructor code is executed, state variables are initialised to
their specified value if you initialise them inline, or zero if you do not.
After the final code of the contract is returned. The final deployment of
the code costs additional gas linear to the length of the code. If you did not
supply enough gas to initiate the state variables declared in the constructor,
then an "out of gas" exception is generated.
Constructor functions can be either ``public`` or ``internal``. If there is no
constructor, the contract will assume the default constructor, which is
equivalent to ``constructor() public {}``. For example:
::
@ -1038,10 +1083,12 @@ derived contracts need to specify all of them. This can be done in two ways::
constructor(uint _x) public { x = _x; }
}
// Either directly specify in the inheritance list...
contract Derived1 is Base(7) {
constructor(uint _y) public {}
constructor() public {}
}
// or through a "modifier" of the derived constructor.
contract Derived2 is Base {
constructor(uint _y) Base(_y * _y) public {}
}
@ -1062,6 +1109,8 @@ contracts' constructors, it will be abstract.
.. index:: ! inheritance;multiple, ! linearization, ! C3 linearization
.. _multi-inheritance:
Multiple Inheritance and Linearization
======================================
@ -1074,18 +1123,23 @@ disallows some inheritance graphs. Especially, the order in
which the base classes are given in the ``is`` directive is
important: You have to list the direct base contracts
in the order from "most base-like" to "most derived".
Note that this order is different from the one used in Python.
Note that this order is the reverse of the one used in Python.
Another simplifying way to explain this is that when a function is called that
is defined multiple times in different contracts, the given bases
are searched from right to left (left to right in Python) in a depth-first manner,
stopping at the first match. If a base contract has already been searched, it is skipped.
In the following code, Solidity will give the
error "Linearization of inheritance graph impossible".
::
// This will not compile
pragma solidity ^0.4.0;
contract X {}
contract A is X {}
// This will not compile
contract C is A, X {}
The reason for this is that ``C`` requests ``X`` to override ``A``
@ -1093,6 +1147,8 @@ The reason for this is that ``C`` requests ``X`` to override ``A``
requests to override ``X``, which is a contradiction that
cannot be resolved.
Inheriting Different Kinds of Members of the Same Name
======================================================
@ -1343,6 +1399,7 @@ custom types without the overhead of external function calls:
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);
}
}

View File

@ -23,8 +23,9 @@ something like::
pragma solidity ^0.4.16;
contract Simple {
function taker(uint _a, uint _b) public pure {
// do something with _a and _b.
uint sum;
function taker(uint _a, uint _b) public {
sum = _a + _b;
}
}
@ -102,7 +103,7 @@ this nonsensical example::
pragma solidity ^0.4.16;
contract C {
function g(uint a) public pure returns (uint ret) { return f(); }
function g(uint a) public pure returns (uint ret) { return a + f(); }
function f() internal pure returns (uint ret) { return g(7) + f(); }
}
@ -158,8 +159,8 @@ throws an exception or goes out of gas.
.. warning::
Any interaction with another contract imposes a potential danger, especially
if the source code of the contract is not known in advance. The current
contract hands over control to the called contract and that may potentially
if the source code of the contract is not known in advance. The
current contract hands over control to the called contract and that may potentially
do just about anything. Even if the called contract inherits from a known parent contract,
the inheriting contract is only required to have a correct interface. The
implementation of the contract, however, can be completely arbitrary and thus,
@ -184,14 +185,16 @@ parameters from the function declaration, but can be in arbitrary order.
pragma solidity ^0.4.0;
contract C {
function f(uint key, uint value) public {
// ...
mapping(uint => uint) data;
function f() public {
set({value: 2, key: 3});
}
function g() public {
// named arguments
f({value: 2, key: 3});
function set(uint key, uint value) public {
data[key] = value;
}
}
Omitted Function Parameter Names
@ -228,7 +231,7 @@ creation-dependencies are not possible.
pragma solidity >0.4.24;
contract D {
uint x;
uint public x;
constructor(uint a) public payable {
x = a;
}
@ -239,11 +242,13 @@ creation-dependencies are not possible.
function createD(uint arg) public {
D newD = new D(arg);
newD.x();
}
function createAndEndowD(uint arg, uint amount) public payable {
// Send ether along with the creation
D newD = (new D).value(amount)(arg);
newD.x();
}
}
@ -287,12 +292,13 @@ These can then either be assigned to newly declared variables or to pre-existing
}
function g() public {
// Variables declared with type and assigned from the returned tuple.
(uint x, bool b, uint y) = f();
// Variables declared with type and assigned from the returned tuple,
// not all elements have to be specified (but the number must match).
(uint x, , uint y) = f();
// Common trick to swap values -- does not work for non-value storage types.
(x, y) = (y, x);
// Components can be left out (also for variable declarations).
(data.length,,) = f(); // Sets the length to 7
(data.length, , ) = f(); // Sets the length to 7
}
}
@ -338,11 +344,13 @@ the two variables have the same name but disjoint scopes.
contract C {
function minimalScoping() pure public {
{
uint same2 = 0;
uint same;
same = 1;
}
{
uint same2 = 0;
uint same;
same = 3;
}
}
}
@ -354,6 +362,7 @@ In any case, you will get a warning about the outer variable being shadowed.
::
pragma solidity >0.4.24;
// This will report a warning
contract C {
function f() pure public returns (uint) {
uint x = 1;
@ -372,9 +381,8 @@ In any case, you will get a warning about the outer variable being shadowed.
::
// This will not compile
pragma solidity >0.4.24;
// This will not compile
contract C {
function f() pure public returns (uint) {
x = 2;
@ -404,11 +412,11 @@ The deprecated keyword ``throw`` can also be used as an alternative to ``revert(
From version 0.4.13 the ``throw`` keyword is deprecated and will be phased out in the future.
When exceptions happen in a sub-call, they "bubble up" (i.e. exceptions are rethrown) automatically. Exceptions to this rule are ``send``
and the low-level functions ``call``, ``delegatecall`` and ``callcode`` -- those return ``false`` in case
and the low-level functions ``call``, ``delegatecall``, ``callcode`` and ``staticcall`` -- those return ``false`` in case
of an exception instead of "bubbling up".
.. warning::
The low-level ``call``, ``delegatecall`` and ``callcode`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
The low-level ``call``, ``delegatecall``, ``callcode`` and ``staticcall`` will return success if the called account is non-existent, as part of the design of EVM. Existence must be checked prior to calling if desired.
Catching exceptions is not yet possible.
@ -447,7 +455,7 @@ A ``require``-style exception is generated in the following situations:
#. Calling ``throw``.
#. Calling ``require`` with an argument that evaluates to ``false``.
#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
#. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall``, ``callcode`` or ``staticcall`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
#. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
#. If you perform an external function call targeting a contract that contains no code.
#. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).

View File

@ -62,7 +62,8 @@ Example::
contract C {
function f() public pure returns (uint8[5] memory) {
string[4] memory adaArr = ["This", "is", "an", "array"];
return ([1, 2, 3, 4, 5]);
adaArr[0] = "That";
return [1, 2, 3, 4, 5];
}
}
@ -186,9 +187,10 @@ If you do not want to throw, you can return a pair::
function checkCounter(uint index) public view {
(uint counter, bool error) = getCounter(index);
if (error) {
// ...
// Handle the error
} else {
// ...
// Do something with counter.
require(counter > 7, "Invalid counter value");
}
}
}
@ -236,13 +238,6 @@ The key point is that the calling contract needs to know about the function it i
See `ping.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_ping.sol>`_
and `pong.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/45_pong.sol>`_.
Get contract to do something when it is first mined
===================================================
Use the constructor. Anything inside it will be executed when the contract is first mined.
See `replicator.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/50_replicator.sol>`_.
How do you create 2-dimensional arrays?
=======================================
@ -372,15 +367,14 @@ contract level) with ``arrayname.length = <some new length>;``. If you get the
::
// This will not compile
pragma solidity ^0.4.18;
// This will not compile
contract C {
int8[] dynamicStorageArray;
int8[5] fixedStorageArray;
function f() {
function f() public {
int8[] memory memArr; // Case 1
memArr.length++; // illegal
@ -409,28 +403,6 @@ Is it possible to return an array of strings (``string[]``) from a Solidity func
Not yet, as this requires two levels of dynamic arrays (``string`` is a dynamic array itself).
If you issue a call for an array, it is possible to retrieve the whole array? Or must you write a helper function for that?
===========================================================================================================================
The automatic :ref:`getter function<getter-functions>` for a public state variable of array type only returns
individual elements. If you want to return the complete array, you have to
manually write a function to do that.
What could have happened if an account has storage value(s) but no code? Example: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5
==========================================================================================================================================================
The last thing a constructor does is returning the code of the contract.
The gas costs for this depend on the length of the code and it might be
that the supplied gas is not enough. This situation is the only one
where an "out of gas" exception does not revert changes to the state,
i.e. in this case the initialisation of the state variables.
https://github.com/ethereum/wiki/wiki/Subtleties
After a successful CREATE operation's sub-execution, if the operation returns x, 5 * len(x) gas is subtracted from the remaining gas before the contract is created. If the remaining gas is less than 5 * len(x), then no gas is subtracted, the code of the created contract becomes the empty string, but this is not treated as an exceptional condition - no reverts happen.
What does the following strange check do in the Custom Token contract?
======================================================================
@ -445,6 +417,25 @@ does not fit inside this range, it is truncated. These truncations can have
above is necessary to avoid certain attacks.
Why are explicit conversions between fixed-size bytes types and integer types failing?
======================================================================================
Since version 0.5.0 explicit conversions between fixed-size byte arrays and integers are only allowed,
if both types have the same size. This prevents unexpected behaviour when truncating or padding.
Such conversions are still possible, but intermediate casts are required that make the desired
truncation and padding convention explicit. See :ref:`types-conversion-elementary-types` for a full
explanation and examples.
Why can number literals not be converted to fixed-size bytes types?
===================================================================
Since version 0.5.0 only hexadecimal number literals can be converted to fixed-size bytes
types and only if the number of hex digits matches the size of the type. See :ref:`types-conversion-literals`
for a full explanation and examples.
More Questions?
===============

View File

@ -60,44 +60,55 @@ Useful links
Available Solidity Integrations
-------------------------------
* `Remix <https://remix.ethereum.org/>`_
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
* Generic:
* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_
Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs)
* `Remix <https://remix.ethereum.org/>`_
Browser-based IDE with integrated compiler and Solidity runtime environment without server-side components.
* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_
Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler.
* `Solium <https://github.com/duaraghav8/Solium/>`_
Linter to identify and fix style and security issues in Solidity.
* `Package for SublimeText — Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_
Solidity syntax highlighting for SublimeText editor.
* `Solhint <https://github.com/protofire/solhint>`_
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
* `Etheratom <https://github.com/0mkara/etheratom>`_
Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible).
* Atom:
* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_
Plugin for the Atom editor that provides Solidity linting.
* `Etheratom <https://github.com/0mkara/etheratom>`_
Plugin for the Atom editor that features syntax highlighting, compilation and a runtime environment (Backend node & VM compatible).
* `Atom Solium Linter <https://atom.io/packages/linter-solium>`_
Configurable Solidty linter for Atom using Solium as a base.
* `Atom Solidity Linter <https://atom.io/packages/linter-solidity>`_
Plugin for the Atom editor that provides Solidity linting.
* `Solium <https://github.com/duaraghav8/Solium/>`_
Linter to identify and fix style and security issues in Solidity.
* `Atom Solium Linter <https://atom.io/packages/linter-solium>`_
Configurable Solidty linter for Atom using Solium as a base.
* `Solhint <https://github.com/protofire/solhint>`_
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
* Emacs:
* `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_
Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler.
* `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_
Plugin for the Emacs editor providing syntax highlighting and compilation error reporting.
* `Emacs Solidity <https://github.com/ethereum/emacs-solidity/>`_
Plugin for the Emacs editor providing syntax highlighting and compilation error reporting.
* IntelliJ:
* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_
Plugin for the Vim editor providing syntax highlighting.
* `IntelliJ IDEA plugin <https://plugins.jetbrains.com/plugin/9475-intellij-solidity>`_
Solidity plugin for IntelliJ IDEA (and all other JetBrains IDEs)
* `Vim Syntastic <https://github.com/scrooloose/syntastic>`_
Plugin for the Vim editor providing compile checking.
* Sublime:
* `Package for SublimeText — Solidity language syntax <https://packagecontrol.io/packages/Ethereum/>`_
Solidity syntax highlighting for SublimeText editor.
* Vim:
* `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_
Plugin for the Vim editor providing syntax highlighting.
* `Vim Syntastic <https://github.com/scrooloose/syntastic>`_
Plugin for the Vim editor providing compile checking.
* Visual Studio Code:
* `Visual Studio Code extension <http://juan.blanco.ws/solidity-contracts-in-visual-studio-code/>`_
Solidity plugin for Microsoft Visual Studio Code that includes syntax highlighting and the Solidity compiler.
Discontinued:
@ -107,10 +118,13 @@ Discontinued:
* `Ethereum Studio <https://live.ether.camp/>`_
Specialized web IDE that also provides shell access to a complete Ethereum environment.
* `Visual Studio Extension <https://visualstudiogallery.msdn.microsoft.com/96221853-33c4-4531-bdd5-d2ea5acc4799/>`_
Solidity plugin for Microsoft Visual Studio that includes the Solidity compiler.
Solidity Tools
--------------
* `Dapp <https://dapp.readthedocs.io>`_
* `Dapp <https://dapp.tools/dapp/>`_
Build tool, package manager, and deployment assistant for Solidity.
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_

View File

@ -100,11 +100,10 @@ When the compiler is invoked, it is not only possible to specify how to
discover the first element of a path, but it is possible to specify path prefix
remappings so that e.g. ``github.com/ethereum/dapp-bin/library`` is remapped to
``/usr/local/dapp-bin/library`` and the compiler will read the files from there.
If multiple remappings can be applied, the one with the longest key is tried first. This
allows for a "fallback-remapping" with e.g. ``""`` maps to
``"/usr/local/include/solidity"``. Furthermore, these remappings can
depend on the context, which allows you to configure packages to
import e.g. different versions of a library of the same name.
If multiple remappings can be applied, the one with the longest key is tried first.
An empty prefix is not allowed. Furthermore, these remappings can depend on the context,
which allows you to configure packages to import e.g. different versions of a library
of the same name.
**solc**:
@ -148,7 +147,7 @@ Note that solc only allows you to include files from certain directories:
They have to be in the directory (or subdirectory) of one of the explicitly
specified source files or in the directory (or subdirectory) of a remapping
target. If you want to allow direct absolute includes, just add the
remapping ``=/``.
remapping ``/=/``.
If there are multiple remappings that lead to a valid file, the remapping
with the longest common prefix is chosen.
@ -205,7 +204,7 @@ for the two input parameters and two returned values.
* @return s The calculated surface.
* @return p The calculated perimeter.
*/
function rectangle(uint w, uint h) public returns (uint s, uint p) {
function rectangle(uint w, uint h) public pure returns (uint s, uint p) {
s = w * h;
p = 2 * (w + h);
}

View File

@ -87,10 +87,14 @@ Solidity always places new objects at the free memory pointer and memory is neve
Layout of Call Data
*******************
When a Solidity contract is deployed and when it is called from an
account, the input data is assumed to be in the format in :ref:`the ABI
specification <ABI>`. The ABI specification requires arguments to be padded to multiples of 32
bytes. The internal function calls use a different convention.
The input data for a function call is assumed to be in the format defined by the :ref:`ABI
specification <ABI>`. Among others, the ABI specification requires arguments to be padded to multiples of 32
bytes. The internal function calls use a different convention.
Arguments for the constructor of a contract are directly appended at the end of the
contract's code, also in ABI encoding. The constructor will access them through a hard-coded offset, and
not by using the ``codesize`` opcode, since this of course changes when appending
data to the code.
.. index: variable cleanup
@ -318,6 +322,7 @@ The following is the order of precedence for operators, listed in order of evalu
Global Variables
================
- ``abi.decode(bytes encodedData, (...)) returns (...)``: :ref:`ABI <ABI>`-decodes the provided data. The types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
- ``abi.encode(...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments
- ``abi.encodePacked(...) returns (bytes)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: :ref:`ABI <ABI>`-encodes the given arguments

View File

@ -171,7 +171,8 @@ before they interact with your contract.
Note that ``.send()`` does **not** throw an exception if the call stack is
depleted but rather returns ``false`` in that case. The low-level functions
``.call()``, ``.callcode()`` and ``.delegatecall()`` behave in the same way.
``.call()``, ``.callcode()``, ``.delegatecall()`` and ``.staticcall()`` behave
in the same way.
tx.origin
=========

View File

@ -90,7 +90,7 @@ of votes.
// If the first argument of `require` evaluates
// to `false`, execution terminates and all
// changes to the state and to Ether balances
// are reverted.
// are reverted.
// This used to consume all gas in old EVM versions, but
// not anymore.
// It is often a good idea to use `require` to check if
@ -467,22 +467,22 @@ high or low invalid bids.
uint refund;
for (uint i = 0; i < length; i++) {
Bid storage bid = bids[msg.sender][i];
Bid storage bidToCheck = bids[msg.sender][i];
(uint value, bool fake, bytes32 secret) =
(_values[i], _fake[i], _secret[i]);
if (bid.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
// Bid was not actually revealed.
// Do not refund deposit.
continue;
}
refund += bid.deposit;
if (!fake && bid.deposit >= value) {
refund += bidToCheck.deposit;
if (!fake && bidToCheck.deposit >= value) {
if (placeBid(msg.sender, value))
refund -= value;
}
// Make it impossible for the sender to re-claim
// the same deposit.
bid.blindedBid = bytes32(0);
bidToCheck.blindedBid = bytes32(0);
}
msg.sender.transfer(refund);
}
@ -708,7 +708,7 @@ For a contract that fulfills payments, the signed message must include:
A replay attack is when a signed message is reused to claim authorization for
a second action.
To avoid replay attacks we will use the same as in Ethereum transactions
themselves, a so-called nonce, which is the number of transactions sent by an
themselves, a so-called nonce, which is the number of transactions sent by an
account.
The smart contract will check if a nonce is used multiple times.
@ -731,7 +731,7 @@ Packing arguments
Now that we have identified what information to include in the
signed message, we are ready to put the message together, hash it,
and sign it. For simplicity, we just concatenate the data.
The
The
`ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ library provides
a function called ``soliditySHA3`` that mimics the behavior
of Solidity's ``keccak256`` function applied to arguments encoded
@ -750,7 +750,7 @@ creates the proper signature for the ``ReceiverPays`` example:
["address", "uint256", "uint256", "address"],
[recipient, amount, nonce, contractAddress]
).toString("hex");
web3.personal.sign(hash, web3.eth.defaultAccount, callback);
}
@ -779,7 +779,7 @@ at the end of this chapter).
Computing the Message Hash
--------------------------
The smart contract needs to know exactly what parameters were signed,
and so it must recreate the message from the parameters and use that
for signature verification. The functions ``prefixed`` and
@ -801,7 +801,7 @@ The full contract
constructor() public payable {}
function claimPayment(uint256 amount, uint256 nonce, bytes signature) public {
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
require(!usedNonces[nonce]);
usedNonces[nonce] = true;
@ -820,7 +820,7 @@ The full contract
}
/// signature methods.
function splitSignature(bytes sig)
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
@ -839,7 +839,7 @@ The full contract
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes sig)
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
@ -874,7 +874,7 @@ two parties (Alice and Bob). Using it involves three steps:
1. Alice funds a smart contract with Ether. This "opens" the payment channel.
2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment.
3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender.
Not ethat only steps 1 and 3 require Ethereum transactions, step 2 means that
the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email).
This means only two transactions are required to support any number of transfers.
@ -906,8 +906,8 @@ Each message includes the following information:
* The smart contract's address, used to prevent cross-contract replay attacks.
* The total amount of Ether that is owed the recipient so far.
A payment channel is closed just once, at the of a series of transfers.
A payment channel is closed just once, at the end of a series of transfers.
Because of this, only one of the messages sent will be redeemed. This is why
each message specifies a cumulative total amount of Ether owed, rather than the
amount of the individual micropayment. The recipient will naturally choose to
@ -926,7 +926,7 @@ Here is the modified javascript code to cryptographically sign a message from th
[contractAddress, amount]
);
}
function signMessage(message, callback) {
web3.personal.sign(
"0x" + message.toString("hex"),
@ -934,10 +934,10 @@ Here is the modified javascript code to cryptographically sign a message from th
callback
);
}
// contractAddress is used to prevent cross-contract replay attacks.
// amount, in wei, specifies how much Ether should be sent.
function signPayment(contractAddress, amount, callback) {
var message = constructPaymentMessage(contractAddress, amount);
signMessage(message, callback);
@ -1003,7 +1003,7 @@ The full contract
expiration = now + duration;
}
function isValidSignature(uint256 amount, bytes signature)
function isValidSignature(uint256 amount, bytes memory signature)
internal
view
returns (bool)
@ -1017,7 +1017,7 @@ The full contract
/// the recipient can close the channel at any time by presenting a
/// signed amount from the sender. the recipient will be sent that amount,
/// and the remainder will go back to the sender
function close(uint256 amount, bytes signature) public {
function close(uint256 amount, bytes memory signature) public {
require(msg.sender == recipient);
require(isValidSignature(amount, signature));
@ -1043,7 +1043,7 @@ The full contract
/// All functions below this are just taken from the chapter
/// 'creating and verifying signatures' chapter.
function splitSignature(bytes sig)
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
@ -1058,11 +1058,11 @@ The full contract
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes sig)
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
@ -1083,7 +1083,7 @@ Note: The function ``splitSignature`` is very simple and does not use all securi
A real implementation should use a more rigorously tested library, such as
openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
Verifying Payments
------------------
@ -1101,7 +1101,7 @@ The recipient should verify each message using the following process:
2. Verify that the new total is the expected amount.
3. Verify that the new total does not exceed the amount of Ether escrowed.
4. Verify that the signature is valid and comes from the payment channel sender.
We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_
library to write this verifications. The final step can be done a number of ways,
but if it's being done in **JavaScript**.
@ -1117,14 +1117,14 @@ above:
["\x19Ethereum Signed Message:\n32", hash]
);
}
function recoverSigner(message, signature) {
var split = ethereumjs.Util.fromRpcSig(signature);
var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s);
var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex");
return signer;
}
function isValidSignature(contractAddress, amount, signature, expectedSigner) {
var message = prefixed(constructPaymentMessage(contractAddress, amount));
var signer = recoverSigner(message, signature);

View File

@ -75,7 +75,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat
_;
}
function abort() public onlySeller { // Modifier usage
function abort() public view onlySeller { // Modifier usage
// ...
}
}

View File

@ -52,31 +52,35 @@ Surround top level declarations in solidity source with two blank lines.
Yes::
pragma solidity ^0.4.0;
contract A {
...
// ...
}
contract B {
...
// ...
}
contract C {
...
// ...
}
No::
pragma solidity ^0.4.0;
contract A {
...
// ...
}
contract B {
...
// ...
}
contract C {
...
// ...
}
Within a contract surround function declarations with a single blank line.
@ -85,30 +89,34 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu
Yes::
pragma solidity ^0.4.0;
contract A {
function spam() public;
function ham() public;
function spam() public pure;
function ham() public pure;
}
contract B is A {
function spam() public {
...
function spam() public pure {
// ...
}
function ham() public {
...
function ham() public pure {
// ...
}
}
No::
pragma solidity ^0.4.0;
contract A {
function spam() public {
...
function spam() public pure {
// ...
}
function ham() public {
...
function ham() public pure {
// ...
}
}
@ -229,22 +237,24 @@ Import statements should always be placed at the top of the file.
Yes::
pragma solidity ^0.4.0;
import "./Owned.sol";
contract A {
...
// ...
}
contract B is Owned {
...
// ...
}
No::
pragma solidity ^0.4.0;
contract A {
...
// ...
}
@ -252,7 +262,7 @@ No::
contract B is Owned {
...
// ...
}
Order of Functions
@ -273,13 +283,15 @@ Within a grouping, place the ``view`` and ``pure`` functions last.
Yes::
pragma solidity ^0.4.0;
contract A {
constructor() public {
...
// ...
}
function() external {
...
// ...
}
// External functions
@ -303,13 +315,15 @@ Yes::
No::
pragma solidity ^0.4.0;
contract A {
// External functions
// ...
function() external {
...
// ...
}
// Private functions
@ -319,7 +333,7 @@ No::
// ...
constructor() public {
...
// ...
}
// Internal functions
@ -397,6 +411,8 @@ should:
Yes::
pragma solidity ^0.4.0;
contract Coin {
struct Bank {
address owner;
@ -406,6 +422,8 @@ Yes::
No::
pragma solidity ^0.4.0;
contract Coin
{
struct Bank {
@ -705,7 +723,25 @@ manner as modifiers if the function declaration is long or hard to read.
Yes::
pragma solidity ^0.4.0;
// Base contracts just to make this compile
contract B {
constructor(uint) public {
}
}
contract C {
constructor(uint, uint) public {
}
}
contract D {
constructor(uint) public {
}
}
contract A is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
@ -713,29 +749,50 @@ Yes::
public
{
// do something with param5
x = param5;
}
}
No::
pragma solidity ^0.4.0;
// Base contracts just to make this compile
contract B {
constructor(uint) public {
}
}
contract C {
constructor(uint, uint) public {
}
}
contract D {
constructor(uint) public {
}
}
contract A is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
public
{
// do something with param5
x = param5;
}
}
contract A is B, C, D {
contract X is B, C, D {
uint x;
constructor(uint param1, uint param2, uint param3, uint param4, uint param5)
B(param1)
C(param2, param3)
D(param4)
public {
// do something with param5
x = param5;
}
}
@ -875,6 +932,8 @@ As shown in the example below, if the contract name is `Congress` and the librar
Yes::
pragma solidity ^0.4.0;
// Owned.sol
contract Owned {
address public owner;
@ -897,11 +956,13 @@ Yes::
import "./Owned.sol";
contract Congress is Owned, TokenRecipient {
...
//...
}
No::
pragma solidity ^0.4.0;
// owned.sol
contract owned {
address public owner;
@ -924,7 +985,7 @@ No::
import "./owned.sol";
contract Congress is owned, tokenRecipient {
...
//...
}

View File

@ -91,7 +91,7 @@ Operators:
defined in the latter. Generally, in floating point almost the entire space is used to represent the number, while only a small number of bits define
where the decimal point is.
.. index:: address, balance, send, call, callcode, delegatecall, transfer
.. index:: address, balance, send, call, callcode, delegatecall, staticcall, transfer
.. _address:
@ -146,7 +146,7 @@ Send is the low-level counterpart of ``transfer``. If the execution fails, the c
to make safe Ether transfers, always check the return value of ``send``, use ``transfer`` or even better:
use a pattern where the recipient withdraws the money.
* ``call``, ``callcode`` and ``delegatecall``
* ``call``, ``callcode``, ``delegatecall`` and ``staticcall``
Furthermore, to interface with contracts that do not adhere to the ABI,
or to get more direct control over the encoding,
@ -189,7 +189,9 @@ Lastly, these modifiers can be combined. Their order does not matter::
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used. Prior to homestead, only a limited variant called ``callcode`` was available that did not provide access to the original ``msg.sender`` and ``msg.value`` values.
All three functions ``call``, ``delegatecall`` and ``callcode`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
Since byzantium ``staticcall`` can be used as well. This is basically the same as ``call``, but will revert, if the called function modifies the state in any way.
All four functions ``call``, ``delegatecall``, ``callcode`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
@ -200,26 +202,39 @@ The ``.gas()`` option is available on all three methods, while the ``.value()``
.. note::
The use of ``callcode`` is discouraged and will be removed in the future.
.. index:: ! contract type, ! type; contract
.. _contract_types:
Contract Types
--------------
Every :ref:`contract<contracts>` defines its own type. Contracts can be implicitly converted
to contracts they inherit from. They can be explicitly converted from and to ``address`` types.
Every :ref:`contract<contracts>` defines its own type.
You can implicitly convert contracts to contracts they inherit from,
and explicitly convert them to and from the ``address`` type.
The data representation of a contract is identical to that of the ``address`` type and
this type is also used in the :ref:`ABI<ABI>`.
.. note::
Starting with version 0.5.0 contracts do not derive from the address type,
but can still be explicitly converted to address.
If you declare a local variable of contract type (`MyContract c`), you can call
functions on that contract. Take care to assign it from somewhere that is the
same contract type.
You can also instantiate contracts (which means they are newly created). You
can find more details in the :ref:`'Contracts via new'<creating-contracts>`
section.
The data representation of a contract is identical to that of the ``address``
type and this type is also used in the :ref:`ABI<ABI>`.
Contracts do not support any operators.
The members of contract types are the external functions of the contract including
public state variables.
.. note::
Starting with version 0.5.0 contracts do not derive from the address type, but can still be explicitly converted to address.
The members of contract types are the external functions of the contract
including public state variables.
.. index:: byte array, bytes32
Fixed-size byte arrays
----------------------
@ -355,6 +370,10 @@ Enums are one way to create a user-defined type in Solidity. They are explicitly
to and from all integer types but implicit conversion is not allowed. The explicit conversions
check the value ranges at runtime and a failure causes an exception. Enums needs at least one member.
The data representation is the same as for enums in C: The options are represented by
subsequent unsigned integer values starting from ``0``.
::
pragma solidity ^0.4.16;
@ -371,7 +390,7 @@ check the value ranges at runtime and a failure causes an exception. Enums need
// Since enum types are not part of the ABI, the signature of "getChoice"
// will automatically be changed to "getChoice() returns (uint8)"
// for all matters external to Solidity. The integer type used is just
// large enough to hold all enum values, i.e. if you have more values,
// large enough to hold all enum values, i.e. if you have more than 256 values,
// `uint16` will be used and so on.
function getChoice() public view returns (ActionChoices) {
return choice;
@ -413,8 +432,26 @@ function type should not return anything, the whole ``returns (<return types>)``
part has to be omitted.
By default, function types are internal, so the ``internal`` keyword can be
omitted. In contrast, contract functions themselves are public by default,
only when used as the name of a type, the default is internal.
omitted. Note that this only applies to function types. Visibility has
to be specified explicitly for functions defined in contracts, they
do not have a default.
A function type ``A`` is implicitly convertible to a function type ``B`` if and only if
their parameter types are identical, their return types are identical,
their internal/external property is identical and the state mutability of ``A``
is not more restrictive than the state mutability of ``B``. In particular:
- ``pure`` functions can be converted to ``view`` and ``non-payable`` functions
- ``view`` functions can be converted to ``non-payable`` functions
- ``payable`` functions can be converted to ``non-payable`` functions
No other conversions are possible.
The rule about ``payable`` and ``non-payable`` might be a little
confusing, but in essence, if a function is ``payable``, this means that it
also accepts a payment of zero Ether, so it also is ``non-payable``.
On the other hand, a ``non-payable`` function will reject Ether sent to it,
so ``non-payable`` functions cannot be converted to ``payable`` functions.
If a function type variable is not initialized, calling it will result
in an exception. The same happens if you call a function after using ``delete``
@ -434,7 +471,7 @@ which returns the :ref:`ABI function selector <abi_function_selector>`::
pragma solidity ^0.4.16;
contract Selector {
function f() public view returns (bytes4) {
function f() public pure returns (bytes4) {
return this.f.selector;
}
}
@ -497,15 +534,15 @@ Another example that uses external function types::
contract Oracle {
struct Request {
bytes data;
function(bytes memory) external callback;
function(uint) external callback;
}
Request[] requests;
event NewRequest(uint);
function query(bytes memory data, function(bytes memory) external callback) public {
function query(bytes memory data, function(uint) external callback) public {
requests.push(Request(data, callback));
emit NewRequest(requests.length - 1);
}
function reply(uint requestID, bytes memory response) public {
function reply(uint requestID, uint response) public {
// Here goes the check that the reply comes from a trusted source
requests[requestID].callback(response);
}
@ -513,15 +550,16 @@ Another example that uses external function types::
contract OracleUser {
Oracle constant oracle = Oracle(0x1234567); // known contract
uint exchangeRate;
function buySomething() public {
oracle.query("USD", this.oracleResponse);
}
function oracleResponse(bytes memory response) public {
function oracleResponse(uint response) public {
require(
msg.sender == address(oracle),
"Only oracle can call this."
);
// Use the data
exchangeRate = response;
}
}
@ -544,16 +582,18 @@ variables are held).
Data location
-------------
Every complex type, i.e. *arrays* and *structs*, has an additional
annotation, the "data location", about whether it is stored in memory or in storage. Depending on the
context, there is always a default, but it can be overridden by appending
either ``storage`` or ``memory`` to the type. The default for function parameters (including return parameters) is ``memory``, the default for local variables is ``storage`` and the location is forced
to ``storage`` for state variables (obviously).
There is also a third data location, ``calldata``, which is a non-modifiable,
non-persistent area where function arguments are stored. Function parameters
(not return parameters) of external functions are forced to ``calldata`` and
behave mostly like ``memory``.
Every complex type, i.e. *arrays* and *structs*, has an additional
annotation, the "data location", about where it is stored. There are three data locations:
``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract
functions and is required for this type of parameter. Calldata is a non-modifiable,
non-persistent area where function arguments are stored, and behaves mostly like memory.
.. note::
Prior to version 0.5.0 the data location could be omitted, and would default to different locations
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
data location.
Data locations are important because they change how assignments behave:
assignments between storage and memory and also to a state variable (even from other state variables)
@ -588,8 +628,8 @@ memory-stored reference type do not create a copy.
h(x); // calls h and creates an independent, temporary copy in memory
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memory memoryArray) public {}
function g(uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
}
Summary
@ -599,10 +639,6 @@ Forced data location:
- parameters (not return) of external functions: calldata
- state variables: storage
Default data location:
- parameters (also return) of functions: memory
- all other local variables: storage
.. index:: ! array
.. _arrays:
@ -646,8 +682,9 @@ Allocating Memory Arrays
^^^^^^^^^^^^^^^^^^^^^^^^
Creating arrays with variable length in memory can be done using the ``new`` keyword.
As opposed to storage arrays, it is **not** possible to resize memory arrays by assigning to
the ``.length`` member.
As opposed to storage arrays, it is **not** possible to resize memory arrays (e.g. by assigning to
the ``.length`` member). You either have to calculate the required size in advance
or create a new memory array and copy every element.
::
@ -657,7 +694,8 @@ the ``.length`` member.
function f(uint len) public pure {
uint[] memory a = new uint[](7);
bytes memory b = new bytes(len);
// Here we have a.length == 7 and b.length == len
assert(a.length == 7);
assert(b.length == len);
a[6] = 8;
}
}
@ -678,7 +716,7 @@ assigned to a variable right away.
function f() public pure {
g([uint(1), 2, 3]);
}
function g(uint[3] memory _data) public pure {
function g(uint[3] memory) public pure {
// ...
}
}
@ -693,10 +731,9 @@ possible:
::
// This will not compile.
pragma solidity ^0.4.0;
// This will not compile.
contract C {
function f() public {
// The next line creates a type error because uint[3] memory
@ -739,9 +776,12 @@ Members
uint[2**20] m_aLotOfIntegers;
// Note that the following is not a pair of dynamic arrays but a
// dynamic array of pairs (i.e. of fixed size arrays of length two).
// Because of that, T[] is always a dynamic array of T, even if T
// itself is an array.
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the default for function arguments
// newPairs is stored in memory - the only possibility
// for public function arguments
function setAllFlagPairs(bool[2][] memory newPairs) public {
// assignment to a storage array replaces the complete array
m_pairsOfFlags = newPairs;
@ -773,7 +813,7 @@ Members
// but can be treated identical to "uint8[]"
m_byteData = data;
m_byteData.length += 7;
m_byteData[3] = byte(8);
m_byteData[3] = 0x08;
delete m_byteData[2];
}
@ -784,6 +824,11 @@ Members
function createMemoryArray(uint size) public pure returns (bytes memory) {
// Dynamic memory arrays are created using `new`:
uint[2][] memory arrayOfPairs = new uint[2][](size);
// Inline arrays are always statically-sized and if you only
// use literals, you have to provide at least one type.
arrayOfPairs[0] = [uint(1), 2];
// Create a dynamic byte array:
bytes memory b = new bytes(200);
for (uint i = 0; i < b.length; i++)
@ -955,11 +1000,14 @@ It is important to note that ``delete a`` really behaves like an assignment to `
// y is affected which is an alias to the storage object
// On the other hand: "delete y" is not valid, as assignments to local variables
// referencing storage objects can only be made from existing storage objects.
assert(y.length == 0);
}
}
.. index:: ! type;conversion, ! cast
.. _types-conversion-elementary-types:
Conversions between Elementary Types
====================================
@ -973,9 +1021,7 @@ is possible if it
makes sense semantically and no information is lost: ``uint8`` is convertible to
``uint16`` and ``int128`` to ``int256``, but ``int8`` is not convertible to ``uint256``
(because ``uint256`` cannot hold e.g. ``-1``).
Furthermore, unsigned integers can be converted to bytes of the same or larger
size, but not vice-versa. Any type that can be converted to ``uint160`` can also
be converted to ``address``.
Any integer type that can be converted to ``uint160`` can also be converted to ``address``.
Explicit Conversions
--------------------
@ -994,17 +1040,90 @@ a negative ``int8`` to a ``uint``:
At the end of this code snippet, ``x`` will have the value ``0xfffff..fd`` (64 hex
characters), which is -3 in the two's complement representation of 256 bits.
If a type is explicitly converted to a smaller type, higher-order bits are
If an integer is explicitly converted to a smaller type, higher-order bits are
cut off::
uint32 a = 0x12345678;
uint16 b = uint16(a); // b will be 0x5678 now
Since 0.5.0 explicit conversions between integers and fixed-size byte arrays
are only allowed, if both have the same size. To convert between integers and
fixed-size byte arrays of different size, they first have to be explicitly
converted to a matching size. This makes alignment and padding explicit::
If an integer is explicitly converted to a larger type, it is padded on the left (i.e. at the higher order end).
The result of the conversion will compare equal to the original integer.
uint16 x = 0xffff;
bytes32(uint256(x)); // pad on the left
bytes32(bytes2(x)); // pad on the right
uint16 a = 0x1234;
uint32 b = uint32(a); // b will be 0x00001234 now
assert(a == b);
Fixed-size bytes types behave differently during conversions. They can be thought of as
sequences of individual bytes and converting to a smaller type will cut off the
sequence::
bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b will be 0x12
If a fixed-size bytes type is explicitly converted to a larger type, it is padded on
the right. Accessing the byte at a fixed index will result in the same value before and
after the conversion (if the index is still in range)::
bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b will be 0x12340000
assert(a[0] == b[0]);
assert(a[1] == b[1]);
Since integers and fixed-size byte arrays behave differently when truncating or
padding, explicit conversions between integers and fixed-size byte arrays are only allowed,
if both have the same size. If you want to convert between integers and fixed-size byte arrays of
different size, you have to use intermediate conversions that make the desired truncation and padding
rules explicit::
bytes2 a = 0x1234;
uint32 b = uint16(a); // b will be 0x00001234
uint32 c = uint32(bytes4(a)); // c will be 0x12340000
uint8 d = uint8(uint16(a)); // d will be 0x34
uint8 e = uint8(bytes1(a)); // d will be 0x12
.. _types-conversion-literals:
Conversions between Literals and Elementary Types
=================================================
Integer Types
-------------
Decimal and hexadecimal number literals can be implicitly converted to any integer type
that is large enough to represent it without truncation::
uint8 a = 12; // fine
uint32 b = 1234; // fine
uint16 c = 0x123456; // fails, since it would have to truncate to 0x3456
Fixed-Size Byte Arrays
----------------------
Decimal number literals cannot be implicitly converted to fixed-size byte arrays. Hexadecimal
number literals can be, but only if the number of hex digits exactly fits the size of the bytes
type. As an exception both decimal and hexadecimal literals which have a value of zero can be
converted to any fixed-size bytes type::
bytes2 a = 54321; // not allowed
bytes2 b = 0x12; // not allowed
bytes2 c = 0x123; // not allowed
bytes2 d = 0x1234; // fine
bytes2 e = 0x0012; // fine
bytes4 f = 0; // fine
bytes4 g = 0x0; // fine
String literals and hex string literals can be implicitly converted to fixed-size byte arrays,
if their number of characters matches the size of the bytes type::
bytes2 a = hex"1234"; // fine
bytes2 b = "xy"; // fine
bytes2 c = hex"12"; // not allowed
bytes2 d = hex"123"; // not allowed
bytes2 e = "x"; // not allowed
bytes2 f = "xyz"; // not allowed
Addresses
---------
As described in :ref:`address_literals`, hex literals of the correct size that pass the checksum
test are of ``address`` type. No other literals can be implicitly converted to the ``address`` type.

View File

@ -96,13 +96,14 @@ Block and Transaction Properties
.. index:: abi, encoding, packed
ABI Encoding Functions
----------------------
ABI Encoding and Decoding Functions
-----------------------------------
- ``abi.decode(bytes encodedData, (...)) returns (...)``: ABI-decodes the given data, while the types are given in parentheses as second argument. Example: ``(uint a, uint[2] memory b, bytes memory c) = abi.decode(data, (uint, uint[2], bytes))``
- ``abi.encode(...) returns (bytes)``: ABI-encodes the given arguments
- ``abi.encodePacked(...) returns (bytes)``: Performs :ref:`packed encoding <abi_packed_mode>` of the given arguments
- ``abi.encodeWithSelector(bytes4 selector, ...) returns (bytes)``: ABI-encodes the given arguments starting from the second and prepends the given four-byte selector
- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature)), ...)```
- ``abi.encodeWithSignature(string signature, ...) returns (bytes)``: Equivalent to ``abi.encodeWithSelector(bytes4(keccak256(bytes(signature))), ...)```
.. note::
These encoding functions can be used to craft data for function calls without actually
@ -152,7 +153,7 @@ Mathematical and Cryptographic Functions
It might be that you run into Out-of-Gas for ``sha256``, ``ripemd160`` or ``ecrecover`` on a *private blockchain*. The reason for this is that those are implemented as so-called precompiled contracts and these contracts only really exist after they received the first message (although their contract code is hardcoded). Messages to non-existing contracts are more expensive and thus the execution runs into an Out-of-Gas error. A workaround for this problem is to first send e.g. 1 Wei to each of the contracts before you use them in your actual contracts. This is not an issue on the official or test net.
.. index:: balance, send, transfer, call, callcode, delegatecall
.. index:: balance, send, transfer, call, callcode, delegatecall, staticcall
.. _address_related:
Address Related
@ -170,6 +171,8 @@ Address Related
issue low-level ``CALLCODE`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
``<address>.delegatecall(bytes memory) returns (bool)``:
issue low-level ``DELEGATECALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
``<address>.staticcall(bytes memory) returns (bool)``:
issue low-level ``STATICCALL`` with the given payload, returns ``false`` on failure, forwards all available gas, adjustable
For more information, see the section on :ref:`address`.

View File

@ -23,14 +23,15 @@ it is also possible to provide path redirects using ``prefix=path`` in the follo
::
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ file.sol
This essentially instructs the compiler to search for anything starting with
``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin`` and if it does not
find the file there, it will look at ``/usr/local/lib/fallback`` (the empty prefix
always matches). ``solc`` will not read files from the filesystem that lie outside of
``github.com/ethereum/dapp-bin/`` under ``/usr/local/lib/dapp-bin``.
``solc`` will not read files from the filesystem that lie outside of
the remapping targets and outside of the directories where explicitly specified source
files reside, so things like ``import "/etc/passwd";`` only work if you add ``=/`` as a remapping.
files reside, so things like ``import "/etc/passwd";`` only work if you add ``/=/`` as a remapping.
An empty remapping prefix is not allowed.
If there are multiple matches due to remappings, the one with the longest common prefix is selected.

View File

@ -29,13 +29,16 @@
using namespace std;
using namespace dev;
bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance)
bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance, size_t _lenThreshold)
{
if (_str1 == _str2)
return true;
size_t n1 = _str1.size();
size_t n2 = _str2.size();
if (_lenThreshold > 0 && n1 * n2 > _lenThreshold)
return false;
size_t distance = stringDistance(_str1, _str2);
// if distance is not greater than _maxDistance, and distance is strictly less than length of both names, they can be considered similar

View File

@ -30,7 +30,8 @@ namespace dev
{
// Calculates the DamerauLevenshtein distance between _str1 and _str2 and returns true if that distance is not greater than _maxDistance
bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance);
// if _lenThreshold > 0 and the product of the strings length is greater than _lenThreshold, the function will return false
bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance, size_t _lenThreshold = 0);
// Calculates the DamerauLevenshtein distance between _str1 and _str2
size_t stringDistance(std::string const& _str1, std::string const& _str2);
// Return a string having elements of suggestions as quoted, alternative suggestions. e.g. "a", "b" or "c"
@ -49,27 +50,23 @@ std::string joinHumanReadable
std::string const& _lastSeparator = ""
)
{
auto it = begin(_list);
auto itEnd = end(_list);
auto const itEnd = end(_list);
std::string result;
// append first string
if (it != itEnd)
for (auto it = begin(_list); it != itEnd; )
{
result += *it;
std::string element = *it;
bool first = (it == begin(_list));
++it;
}
for (;it != itEnd; ++it)
{
if ((std::next(it) == itEnd) && !_lastSeparator.empty())
result += _lastSeparator; // last iteration
else
result += _separator;
// append string
result += *it;
if (!first)
{
if (it == itEnd && !_lastSeparator.empty())
result += _lastSeparator; // last iteration
else
result += _separator;
}
result += std::move(element);
}
return result;

View File

@ -617,8 +617,8 @@ LinkerObject const& Assembly::assemble() const
}
if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty())
// Append a STOP just to be sure.
ret.bytecode.push_back(0);
// Append an INVALID here to help tests find miscompilation.
ret.bytecode.push_back(byte(Instruction::INVALID));
for (size_t i = 0; i < m_subs.size(); ++i)
{

View File

@ -93,6 +93,7 @@ bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items)
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
{
assertThrow(_data.size() > 0, OptimizerException, "Empty bytecode generated.");
if (m_params.isCreation)
{
bigint gas;
@ -101,7 +102,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
return gas;
}
else
return GasCosts::createDataGas * dataSize();
return GasCosts::createDataGas * _data.size();
}
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)

View File

@ -75,8 +75,6 @@ public:
virtual AssemblyItems execute(Assembly& _assembly) const = 0;
protected:
size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); }
/// @returns the run gas for the given items ignoring special gas costs
static bigint simpleRunGas(AssemblyItems const& _items);
/// @returns the gas needed to store the given data literally

View File

@ -75,7 +75,10 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues(
{
auto& unassignedAtFunctionEntry = unassigned[_functionEntry];
for (auto const& returnParameter: _function.returnParameterList()->parameters())
if (returnParameter->type()->dataStoredIn(DataLocation::Storage))
if (
returnParameter->type()->dataStoredIn(DataLocation::Storage) ||
returnParameter->type()->category() == Type::Category::Mapping
)
unassignedAtFunctionEntry.insert(returnParameter.get());
}

View File

@ -138,19 +138,22 @@ vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _n
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
{
static size_t const MAXIMUM_EDIT_DISTANCE = 2;
// because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;)
// since 80 is the suggested line length limit, we use 80^2 as length threshold
static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80;
vector<ASTString> similar;
for (auto const& declaration: m_declarations)
{
string const& declarationName = declaration.first;
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD))
similar.push_back(declarationName);
}
for (auto const& declaration: m_invisibleDeclarations)
{
string const& declarationName = declaration.first;
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD))
similar.push_back(declarationName);
}

View File

@ -48,7 +48,10 @@ bool DocStringAnalyser::visit(ContractDefinition const& _contract)
bool DocStringAnalyser::visit(FunctionDefinition const& _function)
{
handleCallable(_function, _function, _function.annotation());
if (_function.isConstructor())
handleConstructor(_function, _function, _function.annotation());
else
handleCallable(_function, _function, _function.annotation());
return true;
}
@ -66,15 +69,11 @@ bool DocStringAnalyser::visit(EventDefinition const& _event)
return true;
}
void DocStringAnalyser::handleCallable(
void DocStringAnalyser::checkParameters(
CallableDeclaration const& _callable,
Documented const& _node,
DocumentedAnnotation& _annotation
)
{
static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"};
parseDocStrings(_node, _annotation, validTags, "functions");
set<string> validParams;
for (auto const& p: _callable.parameters())
validParams.insert(p->name());
@ -89,6 +88,29 @@ void DocStringAnalyser::handleCallable(
i->second.paramName +
"\" not found in the parameter list of the function."
);
}
void DocStringAnalyser::handleConstructor(
CallableDeclaration const& _callable,
Documented const& _node,
DocumentedAnnotation& _annotation
)
{
static const set<string> validTags = set<string>{"author", "dev", "notice", "param"};
parseDocStrings(_node, _annotation, validTags, "constructor");
checkParameters(_callable, _annotation);
}
void DocStringAnalyser::handleCallable(
CallableDeclaration const& _callable,
Documented const& _node,
DocumentedAnnotation& _annotation
)
{
static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"};
parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _annotation);
}
void DocStringAnalyser::parseDocStrings(

View File

@ -48,6 +48,17 @@ private:
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual bool visit(EventDefinition const& _event) override;
void checkParameters(
CallableDeclaration const& _callable,
DocumentedAnnotation& _annotation
);
void handleConstructor(
CallableDeclaration const& _callable,
Documented const& _node,
DocumentedAnnotation& _annotation
);
void handleCallable(
CallableDeclaration const& _callable,
Documented const& _node,

View File

@ -626,6 +626,17 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName)
{
enterNewSubScope(_funTypeName);
return true;
}
void DeclarationRegistrationHelper::endVisit(FunctionTypeName&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(Block& _block)
{
_block.setScope(m_currentScope);

View File

@ -171,6 +171,8 @@ private:
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
bool visit(FunctionTypeName& _funTypeName) override;
void endVisit(FunctionTypeName& _funTypeName) override;
bool visit(Block& _block) override;
void endVisit(Block& _block) override;
bool visit(ForStatement& _forLoop) override;

View File

@ -30,7 +30,10 @@
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libdevcore/StringUtils.h>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace dev;
@ -155,7 +158,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = make_shared<ContractType>(*contract);
else
{
_typeName.annotation().type = make_shared<TupleType>();
typeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
}
}
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
@ -166,13 +172,13 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
case VariableDeclaration::Visibility::External:
break;
default:
typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
return;
}
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
{
typeError(_typeName.location(), "Only external function types can be payable.");
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
return;
}
@ -182,7 +188,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->canBeUsedExternally(false))
{
typeError(t->location(), "Internal type cannot be used for external function type.");
fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
return;
}
}
@ -300,6 +306,9 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
if (_variable.annotation().type)
return;
if (_variable.isConstant() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
if (!_variable.typeName())
{
// This can still happen in very unusual cases where a developer uses constructs, such as
@ -309,127 +318,92 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
// after this step.
return;
}
TypePointer type;
type = _variable.typeName()->annotation().type;
using Location = VariableDeclaration::Location;
Location varLoc = _variable.referenceLocation();
DataLocation typeLoc = DataLocation::Memory;
// References are forced to calldata for external function parameters (not return)
// and memory for parameters (also return) of publicly visible functions.
// They default to memory for function parameters and storage for local variables.
// As an exception, "storage" is allowed for library functions.
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
set<Location> allowedDataLocations = _variable.allowedDataLocations();
if (!allowedDataLocations.count(varLoc))
{
bool isPointer = true;
if (_variable.isExternalCallableParameter())
auto locationToString = [](VariableDeclaration::Location _location) -> string
{
auto const& contract = dynamic_cast<ContractDefinition const&>(
*dynamic_cast<Declaration const&>(*_variable.scope()).scope()
);
if (contract.isLibrary())
switch (_location)
{
if (varLoc == Location::Memory)
fatalTypeError(_variable.location(),
"Location has to be calldata or storage for external "
"library functions (remove the \"memory\" keyword)."
);
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
case Location::Default: return "none";
}
else
{
// force location of external function parameters (not return) to calldata
if (varLoc != Location::CallData && varLoc != Location::Default)
fatalTypeError(_variable.location(),
"Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)."
);
}
if (varLoc == Location::Default || varLoc == Location::CallData)
typeLoc = DataLocation::CallData;
else
typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
{
auto const& contract = dynamic_cast<ContractDefinition const&>(
*dynamic_cast<Declaration const&>(*_variable.scope()).scope()
);
// force locations of public or external function (return) parameters to memory
if (varLoc != Location::Memory && varLoc != Location::Default && !contract.isLibrary())
fatalTypeError(_variable.location(),
"Location has to be memory for publicly visible functions "
"(remove the \"storage\" or \"calldata\" keyword)."
);
if (varLoc == Location::Default || !contract.isLibrary())
typeLoc = DataLocation::Memory;
else
{
if (varLoc == Location::CallData)
fatalTypeError(_variable.location(),
"Location cannot be calldata for non-external functions "
"(remove the \"calldata\" keyword)."
);
typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
}
return {};
};
string errorString;
if (!_variable.hasReferenceOrMappingType())
errorString = "Data location can only be specified for array, struct or mapping types";
else
{
if (_variable.isConstant())
{
if (varLoc != Location::Default && varLoc != Location::Memory)
fatalTypeError(
_variable.location(),
"Data location has to be \"memory\" (or unspecified) for constants."
);
typeLoc = DataLocation::Memory;
}
else if (varLoc == Location::Default)
{
if (_variable.isCallableParameter())
typeLoc = DataLocation::Memory;
else
{
typeLoc = DataLocation::Storage;
if (_variable.isLocalVariable())
typeError(
_variable.location(),
"Data location must be specified as either \"memory\" or \"storage\"."
);
}
}
errorString = "Data location must be " +
joinHumanReadable(
allowedDataLocations | boost::adaptors::transformed(locationToString),
", ",
" or "
);
if (_variable.isCallableParameter())
errorString +=
" for " +
string(_variable.isReturnParameter() ? "return " : "") +
"parameter in" +
string(_variable.isExternalCallableParameter() ? " external" : "") +
" function";
else
{
switch (varLoc)
{
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
case Location::CallData:
fatalTypeError(_variable.location(),
"Variable cannot be declared as \"calldata\" (remove the \"calldata\" keyword)."
);
break;
default:
solAssert(false, "Unknown data location");
}
}
isPointer = !_variable.isStateVariable();
errorString += " for variable";
}
errorString += ", but " + locationToString(varLoc) + " was given.";
typeError(_variable.location(), errorString);
solAssert(!allowedDataLocations.empty(), "");
varLoc = *allowedDataLocations.begin();
}
// Find correct data location.
if (_variable.isEventParameter())
{
solAssert(varLoc == Location::Default, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Default, "");
typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage;
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
dynamic_cast<EnumDefinition const*>(_variable.scope())
)
// The actual location will later be changed depending on how the type is used.
typeLoc = DataLocation::Storage;
else
switch (varLoc)
{
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Default:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
TypePointer type = _variable.typeName()->annotation().type;
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
bool isPointer = !_variable.isStateVariable();
type = ref->copyForLocation(typeLoc, isPointer);
}
else if (dynamic_cast<MappingType const*>(type.get()))
{
if (_variable.isLocalVariable() && varLoc != Location::Storage)
typeError(
_variable.location(),
"Data location for mappings must be specified as \"storage\"."
);
}
else if (varLoc != Location::Default && !ref)
typeError(_variable.location(), "Data location can only be given for array or struct types.");
_variable.annotation().type = type;
}

View File

@ -525,6 +525,75 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
);
}
TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
{
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
if (arguments.size() != 2)
m_errorReporter.typeError(
_functionCall.location(),
"This function takes two arguments, but " +
toString(arguments.size()) +
" were provided."
);
if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
m_errorReporter.typeError(
arguments.front()->location(),
"Invalid type for argument in function call. "
"Invalid implicit conversion from " +
type(*arguments.front())->toString() +
" to bytes memory requested."
);
TypePointer returnType = make_shared<TupleType>();
if (arguments.size() < 2)
return returnType;
// The following is a rather syntactic restriction, but we check it here anyway:
// The second argument has to be a tuple expression containing type names.
TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get());
if (!tupleExpression)
{
m_errorReporter.typeError(
arguments[1]->location(),
"The second argument to \"abi.decode\" has to be a tuple of types."
);
return returnType;
}
vector<TypePointer> components;
for (auto const& typeArgument: tupleExpression->components())
{
solAssert(typeArgument, "");
if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get()))
{
TypePointer actualType = argTypeType->actualType();
solAssert(actualType, "");
// We force memory because the parser currently cannot handle
// data locations. Furthermore, storage can be a little dangerous and
// calldata is not really implemented anyway.
actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType);
solAssert(
!actualType->dataStoredIn(DataLocation::CallData) &&
!actualType->dataStoredIn(DataLocation::Storage),
""
);
if (!actualType->fullEncodingType(false, _abiEncoderV2, false))
m_errorReporter.typeError(
typeArgument->location(),
"Decoding type " + actualType->toString(false) + " not supported."
);
components.push_back(actualType);
}
else
{
m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name.");
components.push_back(make_shared<TupleType>());
}
}
return make_shared<TupleType>(components);
}
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
{
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
@ -580,9 +649,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
bool TypeChecker::visit(StructDefinition const& _struct)
{
if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces.");
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
if (!type(*member)->canBeStored())
m_errorReporter.typeError(member->location(), "Type cannot be used in struct.");
@ -610,7 +676,10 @@ bool TypeChecker::visit(StructDefinition const& _struct)
if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition.");
bool insideStruct = true;
swap(insideStruct, m_insideStruct);
ASTNode::listAccept(_struct.members(), *this);
m_insideStruct = insideStruct;
return false;
}
@ -629,7 +698,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (!type(*var)->canLiveOutsideStorage())
if (
type(*var)->category() == Type::Category::Mapping &&
!type(*var)->dataStoredIn(DataLocation::Storage)
)
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\".");
else if (
!type(*var)->canLiveOutsideStorage() &&
_function.visibility() > FunctionDefinition::Visibility::Internal
)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
@ -690,10 +767,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
bool TypeChecker::visit(VariableDeclaration const& _variable)
{
// Forbid any variable declarations inside interfaces unless they are part of
// a function's input/output parameters.
// * a function's input/output parameters,
// * or inside of a struct definition.
if (
m_scope->contractKind() == ContractDefinition::ContractKind::Interface
&& !_variable.isCallableParameter()
&& !m_insideStruct
)
m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces.");
@ -711,8 +790,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
expectType(*_variable.value(), *varType);
if (_variable.isConstant())
{
if (!_variable.isStateVariable())
m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
if (!_variable.type()->isValueType())
{
bool allowed = false;
@ -742,7 +819,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
)
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
if (varType->category() == Type::Category::Array)
switch (varType->category())
{
case Type::Category::Array:
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
if (
((arrayType->location() == DataLocation::Memory) ||
@ -750,6 +829,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
!arrayType->validForCalldata()
)
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
break;
case Type::Category::Mapping:
if (auto mappingType = dynamic_cast<MappingType const*>(varType.get()))
if (
mappingType->keyType()->isDynamicallySized() &&
_variable.visibility() == Declaration::Visibility::Public
)
m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported.");
break;
default:
break;
}
return false;
}
@ -818,7 +909,17 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
{
if (var->isIndexed())
{
numIndexed++;
if (
_eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
dynamic_cast<ReferenceType const*>(type(*var).get())
)
m_errorReporter.typeError(
var->location(),
"Indexed reference types cannot yet be used with ABIEncoderV2."
);
}
if (!type(*var)->canLiveOutsideStorage())
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (!type(*var)->interfaceType(false))
@ -1282,7 +1383,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement)
if (
kind == FunctionType::Kind::BareCall ||
kind == FunctionType::Kind::BareCallCode ||
kind == FunctionType::Kind::BareDelegateCall
kind == FunctionType::Kind::BareDelegateCall ||
kind == FunctionType::Kind::BareStaticCall
)
m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used.");
else if (kind == FunctionType::Kind::Send)
@ -1626,10 +1728,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else
{
TypePointer const& argType = type(*arguments.front());
// Resulting data location is memory unless we are converting from a reference
// type with a different data location.
// (data location cannot yet be specified for type conversions)
DataLocation dataLoc = DataLocation::Memory;
if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
// do not change the data location when converting
// (data location cannot yet be specified for type conversions)
resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
dataLoc = argRefType->location();
resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType);
if (!argType->isExplicitlyConvertibleTo(*resultType))
m_errorReporter.typeError(
_functionCall.location(),
@ -1674,6 +1779,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
return false;
}
if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall())
m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version.");
auto returnTypes =
allowDynamicTypes ?
functionType->returnParameterTypes() :
@ -1716,7 +1824,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
if (functionType->kind() == FunctionType::Kind::ABIDecode)
_functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
m_errorReporter.typeError(
@ -1750,7 +1862,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else if (
functionType->kind() == FunctionType::Kind::BareCall ||
functionType->kind() == FunctionType::Kind::BareCallCode ||
functionType->kind() == FunctionType::Kind::BareDelegateCall
functionType->kind() == FunctionType::Kind::BareDelegateCall ||
functionType->kind() == FunctionType::Kind::BareStaticCall
)
{
if (arguments.empty())
@ -1771,8 +1884,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
else if (isPositionalCall)
{
bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
for (size_t i = 0; i < arguments.size(); ++i)
{
auto const& argType = type(*arguments[i]);
@ -1785,7 +1896,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
errored = true;
}
if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments()))
if (!errored && !argType->fullEncodingType(false, abiEncoderV2, !functionType->padArguments()))
m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded.");
}
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
@ -1800,7 +1911,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (
functionType->kind() == FunctionType::Kind::BareCall ||
functionType->kind() == FunctionType::Kind::BareCallCode ||
functionType->kind() == FunctionType::Kind::BareDelegateCall
functionType->kind() == FunctionType::Kind::BareDelegateCall ||
functionType->kind() == FunctionType::Kind::BareStaticCall
)
msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it.";
else if (
@ -2348,22 +2460,6 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte
"."
);
}
if (
type(_expression)->category() == Type::Category::RationalNumber &&
_expectedType.category() == Type::Category::FixedBytes
)
{
auto literal = dynamic_cast<Literal const*>(&_expression);
if (literal && !literal->isHexNumber())
m_errorReporter.warning(
_expression.location(),
"Decimal literal assigned to bytesXX variable will be left-aligned. "
"Use an explicit conversion to silence this warning."
);
}
}
void TypeChecker::requireLValue(Expression const& _expression)

View File

@ -91,6 +91,11 @@ private:
// and reports an error, if not.
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
/// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the
/// return type (which is basically the second argument) if successful. It returns
/// the empty tuple type or error.
TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
virtual bool visit(StructDefinition const& _struct) override;
@ -149,6 +154,9 @@ private:
/// Flag indicating whether we are currently inside an EmitStatement.
bool m_insideEmitStatement = false;
/// Flag indicating whether we are currently inside a StructDefinition.
bool m_insideStruct = false;
ErrorReporter& m_errorReporter;
};

View File

@ -295,7 +295,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{
// we can ignore the kind of magic and only look at the name of the member
set<string> static const pureMembers{
"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash"
"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash"
};
if (!pureMembers.count(member))
mutability = StateMutability::View;

View File

@ -397,7 +397,7 @@ SourceUnit const& Scopable::sourceUnit() const
{
ASTNode const* s = scope();
solAssert(s, "");
// will not always be a declaratoion
// will not always be a declaration
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
s = dynamic_cast<Scopable const*>(s)->scope();
return dynamic_cast<SourceUnit const&>(*s);
@ -418,6 +418,7 @@ bool VariableDeclaration::isLocalVariable() const
{
auto s = scope();
return
dynamic_cast<FunctionTypeName const*>(s) ||
dynamic_cast<CallableDeclaration const*>(s) ||
dynamic_cast<Block const*>(s) ||
dynamic_cast<ForStatement const*>(s);
@ -425,14 +426,18 @@ bool VariableDeclaration::isLocalVariable() const
bool VariableDeclaration::isCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable)
return false;
for (auto const& variable: callable->parameters())
if (variable.get() == this)
return true;
if (callable->returnParameterList())
for (auto const& variable: callable->returnParameterList()->parameters())
if (isReturnParameter())
return true;
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
parameters = &funTypeName->parameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
parameters = &callable->parameters();
if (parameters)
for (auto const& variable: *parameters)
if (variable.get() == this)
return true;
return false;
@ -445,11 +450,16 @@ bool VariableDeclaration::isLocalOrReturn() const
bool VariableDeclaration::isReturnParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable)
return false;
if (callable->returnParameterList())
for (auto const& variable: callable->returnParameterList()->parameters())
vector<ASTPointer<VariableDeclaration>> const* returnParameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
returnParameters = &funTypeName->returnParameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->returnParameterList())
returnParameters = &callable->returnParameterList()->parameters();
if (returnParameters)
for (auto const& variable: *returnParameters)
if (variable.get() == this)
return true;
return false;
@ -457,15 +467,88 @@ bool VariableDeclaration::isReturnParameter() const
bool VariableDeclaration::isExternalCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable || callable->visibility() != Declaration::Visibility::External)
if (!isCallableParameter())
return false;
for (auto const& variable: callable->parameters())
if (variable.get() == this)
return true;
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->visibility() == Declaration::Visibility::External)
return !isReturnParameter();
return false;
}
bool VariableDeclaration::isInternalCallableParameter() const
{
if (!isCallableParameter())
return false;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
return funTypeName->visibility() == Declaration::Visibility::Internal;
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
return callable->visibility() <= Declaration::Visibility::Internal;
return false;
}
bool VariableDeclaration::isLibraryFunctionParameter() const
{
if (!isCallableParameter())
return false;
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
else
return false;
}
bool VariableDeclaration::isEventParameter() const
{
return dynamic_cast<EventDefinition const*>(scope()) != nullptr;
}
bool VariableDeclaration::hasReferenceOrMappingType() const
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
TypePointer const& type = typeName()->annotation().type;
return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type.get());
}
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
{
using Location = VariableDeclaration::Location;
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
return set<Location>{ Location::Default };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
{
set<Location> locations{ Location::CallData };
if (isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isCallableParameter())
{
set<Location> locations{ Location::Memory };
if (isInternalCallableParameter() || isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isLocalVariable())
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
if (typeName()->annotation().type->category() == Type::Category::Mapping)
return set<Location>{ Location::Storage };
else
// TODO: add Location::Calldata once implemented for local variables.
return set<Location>{ Location::Memory, Location::Storage };
}
else
// Struct members etc.
return set<Location>{ Location::Default };
}
TypePointer VariableDeclaration::type() const
{
return annotation().type;
@ -580,7 +663,7 @@ bool Literal::passesAddressChecksum() const
return dev::passesAddressChecksum(value(), true);
}
std::string Literal::getChecksummedAddress() const
string Literal::getChecksummedAddress() const
{
solAssert(isHexNumber(), "Expected hex number");
/// Pad literal to be a proper hex address.

View File

@ -685,6 +685,8 @@ public:
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
/// @returns true iff this variable is the parameter (or return parameter) of a function
/// (or function type name or event) or declared inside a function body.
bool isLocalVariable() const;
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
@ -693,13 +695,27 @@ public:
/// @returns true if this variable is a local variable or return parameter.
bool isLocalOrReturn() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
/// This excludes parameters of external function type names.
bool isExternalCallableParameter() const;
/// @returns true if this variable is a parameter or return parameter of an internal function
/// or a function type of internal visibility.
bool isInternalCallableParameter() const;
/// @returns true iff this variable is a parameter(or return parameter of a library function
bool isLibraryFunctionParameter() const;
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
/// in the body of a function or modifier.
/// @returns true if this variable is a parameter of an event.
bool isEventParameter() const;
/// @returns true if the type of the variable is a reference or mapping type, i.e.
/// array, struct or mapping. These types can take a data location (and often require it).
/// Can only be called after reference resolution.
bool hasReferenceOrMappingType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
/// @returns a set of allowed storage locations for the variable.
std::set<Location> allowedDataLocations() const;
virtual TypePointer type() const override;

View File

@ -355,13 +355,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
{
tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal);
if (get<0>(validLiteral) == true)
return make_shared<RationalNumberType>(get<1>(validLiteral));
else
return TypePointer();
}
return RationalNumberType::forLiteral(_literal);
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
default:
@ -400,17 +394,17 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _p
encodingType = encodingType->interfaceType(_inLibraryCall);
if (encodingType)
encodingType = encodingType->encodingType();
if (auto structType = dynamic_cast<StructType const*>(encodingType.get()))
{
// Structs are fine in the following circumstances:
// - ABIv2 without packed encoding or,
// - storage struct for a library
if (!(
(_encoderV2 && !_packed) ||
(structType->location() == DataLocation::Storage && _inLibraryCall)
))
// Structs are fine in the following circumstances:
// - ABIv2 without packed encoding or,
// - storage struct for a library
if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
return encodingType;
TypePointer baseType = encodingType;
while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get()))
baseType = arrayType->baseType();
if (dynamic_cast<StructType const*>(baseType.get()))
if (!_encoderV2 || _packed)
return TypePointer();
}
return encodingType;
}
@ -627,6 +621,7 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
};
else
@ -779,6 +774,30 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
}
}
TypePointer RationalNumberType::forLiteral(Literal const& _literal)
{
solAssert(_literal.token() == Token::Number, "");
tuple<bool, rational> validLiteral = isValidLiteral(_literal);
if (get<0>(validLiteral))
{
TypePointer compatibleBytesType;
if (_literal.isHexNumber())
{
size_t digitCount = count_if(
_literal.value().begin() + 2, // skip "0x"
_literal.value().end(),
[](unsigned char _c) -> bool { return isxdigit(_c); }
);
// require even number of digits
if (!(digitCount & 1))
compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2);
}
return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType);
}
return TypePointer();
}
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal)
{
rational value;
@ -918,14 +937,7 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
return false;
}
case Category::FixedBytes:
{
FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
if (isFractional())
return false;
if (integerType())
return fixedBytes.numBytes() * 8 >= integerType()->numBits();
return false;
}
return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
default:
return false;
}
@ -933,11 +945,15 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
TypePointer mobType = mobileType();
return
(mobType && mobType->isExplicitlyConvertibleTo(_convertTo)) ||
(!isFractional() && _convertTo.category() == Category::FixedBytes)
;
if (isImplicitlyConvertibleTo(_convertTo))
return true;
else if (_convertTo.category() != Category::FixedBytes)
{
TypePointer mobType = mobileType();
return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo));
}
else
return false;
}
TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
@ -1263,7 +1279,8 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
return shared_ptr<FixedPointType const>();
// This means we round towards zero for positive and negative values.
bigint v = value.numerator() / value.denominator();
if (negative)
if (negative && v != 0)
// modify value to satisfy bit requirements for negative numbers:
// add one bit for sign and decrement because negative numbers can be larger
v = (v - 1) << 1;
@ -2502,6 +2519,7 @@ string FunctionType::richIdentifier() const
case Kind::BareCall: id += "barecall"; break;
case Kind::BareCallCode: id += "barecallcode"; break;
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
case Kind::BareStaticCall: id += "barestaticcall"; break;
case Kind::Creation: id += "creation"; break;
case Kind::Send: id += "send"; break;
case Kind::Transfer: id += "transfer"; break;
@ -2533,6 +2551,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@ -2549,28 +2568,10 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
if (
m_kind != other.m_kind ||
m_stateMutability != other.stateMutability() ||
m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
)
if (!equalExcludingStateMutability(other))
return false;
auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
if (
!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
!equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
)
return false;
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
return false;
if (bound() != other.bound())
return false;
if (bound() && *selfType() != *other.selfType())
if (m_stateMutability != other.stateMutability())
return false;
return true;
}
@ -2586,6 +2587,31 @@ bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
return _convertTo.category() == category();
}
bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (_convertTo.category() != category())
return false;
FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
if (!equalExcludingStateMutability(convertTo))
return false;
// non-payable should not be convertible to payable
if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable)
return false;
// payable should be convertible to non-payable, because you are free to pay 0 ether
if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable)
return true;
// e.g. pure should be convertible to view, but not the other way around.
if (m_stateMutability > convertTo.stateMutability())
return false;
return true;
}
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
{
if (_operator == Token::Value::Delete)
@ -2676,6 +2702,7 @@ unsigned FunctionType::sizeOnStack() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::Internal:
case Kind::ArrayPush:
case Kind::ArrayPop:
@ -2743,6 +2770,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
@ -2843,6 +2871,38 @@ bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const
);
}
bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const
{
if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size())
return false;
return equal(
m_returnParameterTypes.cbegin(),
m_returnParameterTypes.cend(),
_other.m_returnParameterTypes.cbegin(),
[](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
);
}
bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const
{
if (m_kind != _other.m_kind)
return false;
if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other))
return false;
//@todo this is ugly, but cannot be prevented right now
if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet)
return false;
if (bound() != _other.bound())
return false;
solAssert(!bound() || *selfType() == *_other.selfType(), "");
return true;
}
bool FunctionType::isBareCall() const
{
switch (m_kind)
@ -2850,6 +2910,7 @@ bool FunctionType::isBareCall() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::ECRecover:
case Kind::SHA256:
case Kind::RIPEMD160:
@ -2863,6 +2924,17 @@ string FunctionType::externalSignature() const
{
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
switch (kind())
{
case Kind::Internal:
case Kind::External:
case Kind::CallCode:
case Kind::DelegateCall:
case Kind::Event:
break;
default:
solAssert(false, "Invalid function type for requesting external signature.");
}
bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
FunctionTypePointer external = interfaceFunctionType();
@ -2899,7 +2971,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncode ||
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
m_kind == Kind::ABIEncodeWithSignature;
m_kind == Kind::ABIEncodeWithSignature ||
m_kind == Kind::ABIDecode;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@ -2992,6 +3065,7 @@ bool FunctionType::padArguments() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
case Kind::BareStaticCall:
case Kind::SHA256:
case Kind::RIPEMD160:
case Kind::KECCAK256:
@ -3253,6 +3327,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
FunctionType::Kind::ABIEncodeWithSignature,
true,
StateMutability::Pure
)},
{"decode", make_shared<FunctionType>(
TypePointers(),
TypePointers(),
strings{},
strings{},
FunctionType::Kind::ABIDecode,
true,
StateMutability::Pure
)}
});
default:

View File

@ -423,12 +423,12 @@ public:
virtual Category category() const override { return Category::RationalNumber; }
/// @returns true if the literal is a valid integer.
static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
static TypePointer forLiteral(Literal const& _literal);
explicit RationalNumberType(rational const& _value):
m_value(_value)
explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
@ -446,7 +446,8 @@ public:
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> integerType() const;
/// @returns the smallest fixed type that can hold the value or incurs the least precision loss.
/// @returns the smallest fixed type that can hold the value or incurs the least precision loss,
/// unless the value was truncated, then a suitable type will be chosen to indicate such event.
/// If the integer part does not fit, returns an empty pointer.
std::shared_ptr<FixedPointType const> fixedPointType() const;
@ -462,6 +463,13 @@ public:
private:
rational m_value;
/// Bytes type to which the rational can be explicitly converted.
/// Empty for all rationals that are not directly parsed from hex literals.
TypePointer m_compatibleBytesType;
/// @returns true if the literal is a valid integer.
static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
@ -896,6 +904,7 @@ public:
BareCall, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash
BareDelegateCall, ///< DELEGATECALL without function hash
BareStaticCall, ///< STATICCALL without function hash
Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
@ -926,7 +935,8 @@ public:
ABIEncodePacked,
ABIEncodeWithSelector,
ABIEncodeWithSignature,
GasLeft ///< gasleft()
ABIDecode,
GasLeft, ///< gasleft()
};
virtual Category category() const override { return Category::Function; }
@ -1005,6 +1015,7 @@ public:
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
@ -1034,10 +1045,14 @@ public:
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
/// @returns true if the types of parameters are equal (doesn't check return parameter types)
/// @returns true if the types of parameters are equal (does not check return parameter types)
bool hasEqualParameterTypes(FunctionType const& _other) const;
/// @returns true iff the return types are equal (does not check parameter types)
bool hasEqualReturnTypes(FunctionType const& _other) const;
/// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
bool equalExcludingStateMutability(FunctionType const& _other) const;
/// @returns true if the ABI is used for this call (only meaningful for external calls)
/// @returns true if the ABI is NOT used for this call (only meaningful for external calls)
bool isBareCall() const;
Kind const& kind() const { return m_kind; }
StateMutability stateMutability() const { return m_stateMutability; }
@ -1076,6 +1091,7 @@ public:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
return true;
default:
return false;

View File

@ -571,6 +571,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
_functionCall.expression().accept(*this);
appendExternalFunctionCall(function, arguments);
break;
@ -1070,6 +1071,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// stack now: <memory pointer>
break;
}
case FunctionType::Kind::ABIDecode:
{
arguments.front()->accept(*this);
TypePointer firstArgType = arguments.front()->annotation().type;
TypePointers const& targetTypes = dynamic_cast<TupleType const&>(*_functionCall.annotation().type).components();
if (
*firstArgType == ArrayType(DataLocation::CallData) ||
*firstArgType == ArrayType(DataLocation::CallData, true)
)
utils().abiDecode(targetTypes, false);
else
{
utils().convertType(*firstArgType, ArrayType(DataLocation::Memory));
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
m_context << Instruction::SWAP1 << Instruction::MLOAD;
// stack now: <mem_pos> <length>
utils().abiDecode(targetTypes, true);
}
break;
}
case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS;
break;
@ -1143,18 +1165,19 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
solAssert(false, "event not found");
// no-op, because the parent node will do the job
break;
case FunctionType::Kind::DelegateCall:
_memberAccess.expression().accept(*this);
m_context << funType->externalIdentifier();
break;
case FunctionType::Kind::CallCode:
case FunctionType::Kind::External:
case FunctionType::Kind::Creation:
case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::CallCode:
case FunctionType::Kind::Send:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
case FunctionType::Kind::Transfer:
_memberAccess.expression().accept(*this);
m_context << funType->externalIdentifier();
break;
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
@ -1252,7 +1275,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
);
m_context << Instruction::BALANCE;
}
else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member))
utils().convertType(
*_memberAccess.expression().annotation().type,
IntegerType(160, IntegerType::Modifier::Address),
@ -1804,10 +1827,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
auto funKind = _functionType.kind();
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool useStaticCall = _functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
unsigned retSize = 0;

View File

@ -58,22 +58,31 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
void CompilerStack::setRemappings(vector<string> const& _remappings)
boost::optional<CompilerStack::Remapping> CompilerStack::parseRemapping(string const& _remapping)
{
auto eq = find(_remapping.begin(), _remapping.end(), '=');
if (eq == _remapping.end())
return {};
auto colon = find(_remapping.begin(), eq, ':');
Remapping r;
r.context = colon == eq ? string() : string(_remapping.begin(), colon);
r.prefix = colon == eq ? string(_remapping.begin(), eq) : string(colon + 1, eq);
r.target = string(eq + 1, _remapping.end());
if (r.prefix.empty())
return {};
return r;
}
void CompilerStack::setRemappings(vector<Remapping> const& _remappings)
{
vector<Remapping> remappings;
for (auto const& remapping: _remappings)
{
auto eq = find(remapping.begin(), remapping.end(), '=');
if (eq == remapping.end())
continue; // ignore
auto colon = find(remapping.begin(), eq, ':');
Remapping r;
r.context = colon == eq ? string() : string(remapping.begin(), colon);
r.prefix = colon == eq ? string(remapping.begin(), eq) : string(colon + 1, eq);
r.target = string(eq + 1, remapping.end());
remappings.push_back(r);
}
swap(m_remappings, remappings);
solAssert(!remapping.prefix.empty(), "");
m_remappings = _remappings;
}
void CompilerStack::setEVMVersion(EVMVersion _version)

View File

@ -84,6 +84,13 @@ public:
CompilationSuccessful
};
struct Remapping
{
std::string context;
std::string prefix;
std::string target;
};
/// Creates a new compiler stack.
/// @param _readFile callback to used to read files for import statements. Must return
/// and must not emit exceptions.
@ -103,8 +110,11 @@ public:
/// All settings, with the exception of remappings, are reset.
void reset(bool _keepSources = false);
/// Sets path remappings in the format "context:prefix=target"
void setRemappings(std::vector<std::string> const& _remappings);
// Parses a remapping of the format "context:prefix=target".
static boost::optional<Remapping> parseRemapping(std::string const& _remapping);
/// Sets path remappings.
void setRemappings(std::vector<Remapping> const& _remappings);
/// Sets library addresses. Addresses are cleared iff @a _libraries is missing.
/// Will not take effect before running compile.
@ -319,13 +329,6 @@ private:
FunctionDefinition const& _function
) const;
struct Remapping
{
std::string context;
std::string prefix;
std::string target;
};
ReadCallback::Callback m_readFile;
ReadCallback::Callback m_smtQuery;
bool m_optimize = false;

View File

@ -36,6 +36,15 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
Json::Value doc;
Json::Value methods(Json::objectValue);
auto constructorDefinition(_contractDef.constructor());
if (constructorDefinition)
{
string value = extractDoc(constructorDefinition->annotation().docTags, "notice");
if (!value.empty())
// add the constructor, only if we have any documentation to add
methods["constructor"] = Json::Value(value);
}
string notice = extractDoc(_contractDef.annotation().docTags, "notice");
if (!notice.empty())
doc["notice"] = Json::Value(notice);
@ -73,33 +82,21 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
if (!dev.empty())
doc["details"] = Json::Value(dev);
auto constructorDefinition(_contractDef.constructor());
if (constructorDefinition) {
Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags));
if (!constructor.empty())
// add the constructor, only if we have any documentation to add
methods["constructor"] = constructor;
}
for (auto const& it: _contractDef.interfaceFunctions())
{
if (!it.second->hasDeclaration())
continue;
Json::Value method;
if (auto fun = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
{
auto dev = extractDoc(fun->annotation().docTags, "dev");
if (!dev.empty())
method["details"] = Json::Value(dev);
auto author = extractDoc(fun->annotation().docTags, "author");
if (!author.empty())
method["author"] = author;
auto ret = extractDoc(fun->annotation().docTags, "return");
if (!ret.empty())
method["return"] = ret;
Json::Value params(Json::objectValue);
auto paramRange = fun->annotation().docTags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i)
params[i->second.paramName] = Json::Value(i->second.content);
if (!params.empty())
method["params"] = params;
Json::Value method(devDocumentation(fun->annotation().docTags));
if (!method.empty())
// add the function, only if we have any documentation to add
methods[it.second->externalSignature()] = method;
@ -118,3 +115,31 @@ string Natspec::extractDoc(multimap<string, DocTag> const& _tags, string const&
value += i->second.content;
return value;
}
Json::Value Natspec::devDocumentation(std::multimap<std::string, DocTag> const &_tags)
{
Json::Value json(Json::objectValue);
auto dev = extractDoc(_tags, "dev");
if (!dev.empty())
json["details"] = Json::Value(dev);
auto author = extractDoc(_tags, "author");
if (!author.empty())
json["author"] = author;
// for constructors, the "return" node will never exist. invalid tags
// will already generate an error within dev::solidity::DocStringAnalyzer.
auto ret = extractDoc(_tags, "return");
if (!ret.empty())
json["return"] = ret;
Json::Value params(Json::objectValue);
auto paramRange = _tags.equal_range("param");
for (auto i = paramRange.first; i != paramRange.second; ++i)
params[i->second.paramName] = Json::Value(i->second.content);
if (!params.empty())
json["params"] = params;
return json;
}

View File

@ -54,6 +54,12 @@ public:
private:
/// @returns concatenation of all content under the given tag name.
static std::string extractDoc(std::multimap<std::string, DocTag> const& _tags, std::string const& _name);
/// Helper-function that will create a json object with dev specific annotations, if present.
/// @param _tags docTags that are used.
/// @return A JSON representation
/// of the contract's developer documentation
static Json::Value devDocumentation(std::multimap<std::string, DocTag> const &_tags);
};
} //solidity NS

View File

@ -326,9 +326,14 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setEVMVersion(*version);
}
vector<string> remappings;
vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
remappings.push_back(remapping.asString());
{
if (auto r = CompilerStack::parseRemapping(remapping.asString()))
remappings.emplace_back(std::move(*r));
else
return formatFatalError("JSONError", "Invalid remapping: \"" + remapping.asString() + "\"");
}
m_compilerStack.setRemappings(remappings);
Json::Value optimizerSettings = settings.get("optimizer", Json::Value());

View File

@ -35,43 +35,26 @@ def extract_test_cases(path):
return tests
# Contract sources are indented by 4 spaces.
# Look for `pragma solidity` and abort a line not indented properly.
# If the comment `// This will not compile` is above the pragma,
# the code is skipped.
# Look for `pragma solidity`, `contract`, `library` or `interface`
# and abort a line not indented properly.
def extract_docs_cases(path):
# Note: this code works, because splitlines() removes empty new lines
# and thus even if the empty new lines are missing indentation
lines = open(path, 'rb').read().splitlines()
ignore = False
inside = False
tests = []
for l in lines:
if inside:
# Abort if indentation is missing
m = re.search(r'^[^ ]+', l)
if m:
inside = False
else:
tests[-1] += l + '\n'
else:
m = re.search(r'^ // This will not compile', l)
if m:
ignore = True
if ignore:
# Abort if indentation is missing
m = re.search(r'^[^ ]+', l)
if m:
ignore = False
else:
m = re.search(r'^ pragma solidity .*[0-9]+\.[0-9]+\.[0-9]+;$', l)
if m:
inside = True
tests += [l]
return tests
# Collect all snippets of indented blocks
for l in open(path, 'rb').read().splitlines():
if l != '':
if not inside and l.startswith(' '):
# start new test
tests += ['']
inside = l.startswith(' ')
if inside:
tests[-1] += l + '\n'
# Filter all tests that do not contain Solidity
return [
test for test in tests
if re.search(r'^ [ ]*(pragma solidity|contract |library |interface )', test, re.MULTILINE)
]
def write_cases(tests):
for test in tests:

View File

@ -392,7 +392,18 @@ bool CommandLineInterface::readInputFilesAndConfigureRemappings()
{
auto eq = find(path.begin(), path.end(), '=');
if (eq != path.end())
path = string(eq + 1, path.end());
{
if (auto r = CompilerStack::parseRemapping(path))
{
m_remappings.emplace_back(std::move(*r));
path = string(eq + 1, path.end());
}
else
{
cerr << "Invalid remapping: \"" << path << "\"." << endl;
return false;
}
}
else if (path == "-")
addStdin = true;
else
@ -808,7 +819,7 @@ bool CommandLineInterface::processInput()
if (m_args.count(g_argMetadataLiteral) > 0)
m_compiler->useMetadataLiteralSources(true);
if (m_args.count(g_argInputFile))
m_compiler->setRemappings(m_args[g_argInputFile].as<vector<string>>());
m_compiler->setRemappings(m_remappings);
for (auto const& sourceCode: m_sourceCodes)
m_compiler->addSource(sourceCode.first, sourceCode.second);
if (m_args.count(g_argLibraries))

View File

@ -97,6 +97,8 @@ private:
boost::program_options::variables_map m_args;
/// map of input files to source code strings
std::map<std::string, std::string> m_sourceCodes;
/// list of remappings
std::vector<dev::solidity::CompilerStack::Remapping> m_remappings;
/// list of allowed directories to read files from
std::vector<boost::filesystem::path> m_allowedDirectories;
/// map of library names to addresses

View File

@ -85,6 +85,22 @@ std::pair<bool, string> ExecutionFramework::compareAndCreateMessage(
return make_pair(false, message);
}
u256 ExecutionFramework::gasLimit() const
{
auto latestBlock = m_rpc.eth_getBlockByNumber("latest", false);
return u256(latestBlock["gasLimit"].asString());
}
u256 ExecutionFramework::gasPrice() const
{
return u256(m_rpc.eth_gasPrice());
}
u256 ExecutionFramework::blockHash(u256 const& _blockNumber) const
{
return u256(m_rpc.eth_getBlockByNumber(toHex(_blockNumber, HexPrefix::Add), false)["hash"].asString());
}
void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value)
{
if (m_showMessages)

View File

@ -194,6 +194,13 @@ public:
return encodeArgs(u256(0x20), u256(_arg.size()), _arg);
}
u256 gasLimit() const;
u256 gasPrice() const;
u256 blockHash(u256 const& _blockNumber) const;
u256 const& blockNumber() const {
return m_blockNumber;
}
private:
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)

View File

@ -207,6 +207,11 @@ string RPCSession::eth_getStorageRoot(string const& _address, string const& _blo
return rpcCall("eth_getStorageRoot", { quote(address), quote(_blockNumber) }).asString();
}
string RPCSession::eth_gasPrice()
{
return rpcCall("eth_gasPrice").asString();
}
void RPCSession::personal_unlockAccount(string const& _address, string const& _password, int _duration)
{
BOOST_REQUIRE_MESSAGE(

View File

@ -113,6 +113,7 @@ public:
std::string eth_sendTransaction(std::string const& _transaction);
std::string eth_getBalance(std::string const& _address, std::string const& _blockNumber);
std::string eth_getStorageRoot(std::string const& _address, std::string const& _blockNumber);
std::string eth_gasPrice();
std::string personal_newAccount(std::string const& _password);
void personal_unlockAccount(std::string const& _address, std::string const& _password, int _duration);
void test_setChainParams(std::vector<std::string> const& _accounts);

45
test/buglistTests.js Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/env node
"use strict";
var fs = require('fs')
var bugs = JSON.parse(fs.readFileSync(__dirname + '/../docs/bugs.json', 'utf8'))
var bugsByName = {}
for (var i in bugs)
{
if (bugs[i].name in bugsByName)
{
throw "Duplicate bug name: " + bugs[i].name
}
bugsByName[bugs[i].name] = bugs[i]
}
var tests = fs.readFileSync(__dirname + '/buglist_test_vectors.md', 'utf8')
var testVectorParser = /\s*#\s+(\S+)\s+## buggy\n([^#]*)## fine\n([^#]*)/g
var result;
while ((result = testVectorParser.exec(tests)) !== null)
{
var name = result[1]
var buggy = result[2].split('\n--\n')
var fine = result[3].split('\n--\n')
console.log("Testing " + name + " with " + buggy.length + " buggy and " + fine.length + " fine instances")
var regex = RegExp(bugsByName[name].check['regex-source'])
for (var i in buggy)
{
if (!regex.exec(buggy[i]))
{
throw "Bug " + name + ": Buggy source does not match: " + buggy[i]
}
}
for (var i in fine)
{
if (regex.exec(fine[i]))
{
throw "Bug " + name + ": Non-buggy source matches: " + fine[i]
}
}
}

View File

@ -0,0 +1,69 @@
# NestedArrayFunctionCallDecoder
## buggy
function f() pure returns (uint[2][2]) { }
--
function f() returns (uint[2][2] a) { }
--
function f() returns (uint x, uint[200][2] a) { }
--
function f() returns (uint[200][2] a, uint x) { }
--
function f() returns (uint[200][2] a, uint x);
--
function f() returns (
uint
[
200
]
[2]
a, uint x);
--
function f() returns (
uint
[
ContractName.ConstantName
]
[2]
a, uint x);
## fine
function f() returns (uint[2]) { }
--
function f() public pure returns (uint[2][] a) { }
--
function f() public pure returns (uint[ 2 ] [ ] a) { }
--
function f() public pure returns (uint x, uint[] a) { }
--
function f(uint[2][2]) { }
--
function f() m(uint[2][2]) { }
--
function f() returns (uint, uint) { uint[2][2] memory x; }

View File

@ -43,18 +43,42 @@ function printError() { echo "$(tput setaf 1)$1$(tput sgr0)"; }
function compileFull()
{
local expected_exit_code=0
local expect_output=0
if [[ $1 = '-e' ]]
then
expected_exit_code=1
expect_output=1
shift;
fi
if [[ $1 = '-w' ]]
then
expect_output=1
shift;
fi
local files="$*"
local output failed
local output
local stderr_path=$(mktemp)
set +e
output=$( ("$SOLC" $FULLARGS $files) 2>&1 )
failed=$?
"$SOLC" $FULLARGS $files >/dev/null 2>"$stderr_path"
local exit_code=$?
local errors=$(grep -v -E 'Warning: This is a pre-release compiler version|Warning: Experimental features are turned on|pragma experimental ABIEncoderV2|\^-------------------------------\^' < "$stderr_path")
set -e
rm "$stderr_path"
if [ $failed -ne 0 ]
if [[ \
"$exit_code" -ne "$expected_exit_code" || \
( $expect_output -eq 0 && -n "$errors" ) || \
( $expect_output -ne 0 && -z "$errors" ) \
]]
then
printError "Compilation failed on:"
echo "$output"
printError "Unexpected compilation result:"
printError "Expected failure: $expected_exit_code - Expected warning / error output: $expect_output"
printError "Was failure: $exit_code"
echo "$errors"
printError "While calling:"
echo "\"$SOLC\" $FULLARGS $files"
printError "Inside directory:"
@ -63,22 +87,6 @@ function compileFull()
fi
}
function compileWithoutWarning()
{
local files="$*"
local output failed
set +e
output=$("$SOLC" $files 2>&1)
failed=$?
# Remove the pre-release warning from the compiler output
output=$(echo "$output" | grep -v 'pre-release')
echo "$output"
set -e
test -z "$output" -a "$failed" -eq 0
}
printTask "Testing unknown options..."
(
set +e
@ -144,6 +152,10 @@ test_solc_file_input_failures "file_not_found.sol" "" "" "\"file_not_found.sol\"
printTask "Testing passing files that are not files..."
test_solc_file_input_failures "." "" "" "\".\" is not a valid file."
printTask "Testing passing empty remappings..."
test_solc_file_input_failures "${0}" "=/some/remapping/target" "" "Invalid remapping: \"=/some/remapping/target\"."
test_solc_file_input_failures "${0}" "ctx:=/some/remapping/target" "" "Invalid remapping: \"ctx:=/some/remapping/target\"."
printTask "Compiling various other contracts and libraries..."
(
cd "$REPO_ROOT"/test/compilationTests/
@ -153,7 +165,7 @@ do
then
echo " - $dir"
cd "$dir"
compileFull *.sol */*.sol
compileFull -w *.sol */*.sol
cd ..
fi
done
@ -169,8 +181,25 @@ TMPDIR=$(mktemp -d)
"$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs
for f in *.sol
do
# The contributors guide uses syntax tests, but we cannot
# really handle them here.
if grep -E 'DeclarationError:|// ----' "$f" >/dev/null
then
continue
fi
echo "$f"
compileFull "$TMPDIR/$f"
opts=''
# We expect errors if explicitly stated, or if imports
# are used (in the style guide)
if grep -E "This will not compile|import \"" "$f" >/dev/null
then
opts="-e"
fi
if grep "This will report a warning" "$f" >/dev/null
then
opts="$opts -w"
fi
compileFull $opts "$TMPDIR/$f"
done
)
rm -rf "$TMPDIR"

View File

@ -140,7 +140,7 @@ contract moduleHandler is multiOwner, announcementTypes {
}
return (true, false, 0);
}
function replaceModule(string name, address addr, bool callCallback) external returns (bool success) {
function replaceModule(string calldata name, address addr, bool callCallback) external returns (bool success) {
/*
Module replace, can be called only by the Publisher contract.
@ -167,7 +167,7 @@ contract moduleHandler is multiOwner, announcementTypes {
return true;
}
function callReplaceCallback(string moduleName, address newModule) external returns (bool success) {
function callReplaceCallback(string calldata moduleName, address newModule) external returns (bool success) {
require( block.number < debugModeUntil );
if ( ! insertAndCheckDo(calcDoHash("callReplaceCallback", keccak256(abi.encodePacked(moduleName, newModule)))) ) {
return true;
@ -178,7 +178,7 @@ contract moduleHandler is multiOwner, announcementTypes {
return true;
}
function newModule(string name, address addr, bool schellingEvent, bool transferEvent) external returns (bool success) {
function newModule(string calldata name, address addr, bool schellingEvent, bool transferEvent) external returns (bool success) {
/*
Adding new module to the database. Can be called only by the Publisher contract.
@ -199,7 +199,7 @@ contract moduleHandler is multiOwner, announcementTypes {
addModule( modules_s(addr, keccak256(bytes(name)), schellingEvent, transferEvent), true);
return true;
}
function dropModule(string name, bool callCallback) external returns (bool success) {
function dropModule(string calldata name, bool callCallback) external returns (bool success) {
/*
Deleting module from the database. Can be called only by the Publisher contract.
@ -224,7 +224,7 @@ contract moduleHandler is multiOwner, announcementTypes {
return true;
}
function callDisableCallback(string moduleName) external returns (bool success) {
function callDisableCallback(string calldata moduleName) external returns (bool success) {
require( block.number < debugModeUntil );
if ( ! insertAndCheckDo(calcDoHash("callDisableCallback", keccak256(bytes(moduleName)))) ) {
return true;
@ -406,7 +406,7 @@ contract moduleHandler is multiOwner, announcementTypes {
require( token(modules[_id].addr).burn(from, value) );
return true;
}
function configureModule(string moduleName, announcementType aType, uint256 value) external returns (bool success) {
function configureModule(string calldata moduleName, announcementType aType, uint256 value) external returns (bool success) {
/*
Changing configuration of a module. Can be called only by Publisher or while debug mode by owners.

View File

@ -5,8 +5,8 @@ import "./tokenDB.sol";
import "./module.sol";
contract thirdPartyPContractAbstract {
function receiveCorionPremiumToken(address, uint256, bytes) external returns (bool, uint256) {}
function approvedCorionPremiumToken(address, uint256, bytes) external returns (bool) {}
function receiveCorionPremiumToken(address, uint256, bytes calldata) external returns (bool, uint256) {}
function approvedCorionPremiumToken(address, uint256, bytes calldata) external returns (bool) {}
}
contract ptokenDB is tokenDB {}
@ -108,7 +108,7 @@ contract premium is module, safeMath {
* @param extraData Data to give forward to the receiver
* @return True if the approval was successful
*/
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes extraData) isReady external returns (bool success) {
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes calldata extraData) isReady external returns (bool success) {
/*
Authorize another address to use an exact amount of the principals balance.
After the transaction the approvedCorionPremiumToken function of the address will be called with the given data.
@ -226,7 +226,7 @@ contract premium is module, safeMath {
* @param extraData Data to give forward to the receiver
* @return Whether the transfer was successful or not
*/
function transfer(address to, uint256 amount, bytes extraData) isReady external returns (bool success) {
function transfer(address to, uint256 amount, bytes calldata extraData) isReady external returns (bool success) {
/*
Launch a transaction where we transfer from a given address to another one.
After thetransaction the approvedCorionPremiumToken function of the receivers address is going to be called with the given data.

View File

@ -213,7 +213,7 @@ contract provider is module, safeMath, announcementTypes {
return ( ! priv && ( rate >= publicMinRate && rate <= publicMaxRate ) ) ||
( priv && ( rate >= privateMinRate && rate <= privateMaxRate ) );
}
function createProvider(bool priv, string name, string website, string country, string info, uint8 rate, bool isForRent, address admin) isReady external {
function createProvider(bool priv, string calldata name, string calldata website, string calldata country, string calldata info, uint8 rate, bool isForRent, address admin) isReady external {
/*
Creating a provider.
During the ICO its not allowed to create provider.
@ -270,7 +270,7 @@ contract provider is module, safeMath, announcementTypes {
}
emit EProviderOpen(msg.sender, currHeight);
}
function setProviderDetails(address addr, string website, string country, string info, uint8 rate, address admin) isReady external {
function setProviderDetails(address addr, string calldata website, string calldata country, string calldata info, uint8 rate, address admin) isReady external {
/*
Modifying the datas of the provider.
This can only be invited by the providers admin.
@ -369,7 +369,7 @@ contract provider is module, safeMath, announcementTypes {
setRightForInterest(getProviderCurrentSupply(msg.sender), 0, providers[msg.sender].data[currHeight].priv);
emit EProviderClose(msg.sender, currHeight);
}
function allowUsers(address provider, address[] addr) isReady external {
function allowUsers(address provider, address[] calldata addr) isReady external {
/*
Permition of the user to be able to connect to the provider.
This can only be invited by the providers admin.
@ -387,7 +387,7 @@ contract provider is module, safeMath, announcementTypes {
providers[provider].data[currHeight].allowedUsers[addr[a]] = true;
}
}
function disallowUsers(address provider, address[] addr) isReady external {
function disallowUsers(address provider, address[] calldata addr) isReady external {
/*
Disable of the user not to be able to connect to the provider.
It is can called only for the admin of the provider.

View File

@ -116,7 +116,7 @@ contract publisher is announcementTypes, module, safeMath {
return _amount * oppositeRate / 100 > weight;
}
function newAnnouncement(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external {
function newAnnouncement(announcementType Type, string calldata Announcement, string calldata Link, bool Oppositable, string calldata _str, uint256 _uint, address _addr) onlyOwner external {
/*
New announcement. Can be called only by those in the admin list

View File

@ -310,7 +310,7 @@ contract schelling is module, announcementTypes, schellingVars {
setRound(currentRound, round);
}
function sendVote(string vote) isReady noContract external {
function sendVote(string calldata vote) isReady noContract external {
/*
Check vote (Envelope opening)
Only the sent envelopes can be opened.

View File

@ -7,8 +7,8 @@ import "./moduleHandler.sol";
import "./tokenDB.sol";
contract thirdPartyContractAbstract {
function receiveCorionToken(address, uint256, bytes) external returns (bool, uint256) {}
function approvedCorionToken(address, uint256, bytes) external returns (bool) {}
function receiveCorionToken(address, uint256, bytes calldata) external returns (bool, uint256) {}
function approvedCorionToken(address, uint256, bytes calldata) external returns (bool) {}
}
contract token is safeMath, module, announcementTypes {
@ -123,7 +123,7 @@ contract token is safeMath, module, announcementTypes {
* @param extraData Data to give forward to the receiver
* @return True if the approval was successful
*/
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes extraData) isReady external returns (bool success) {
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes calldata extraData) isReady external returns (bool success) {
/*
Authorise another address to use a certain quantity of the authorising owners balance
Following the transaction the receiver address `approvedCorionToken` function is called by the given data
@ -267,7 +267,7 @@ contract token is safeMath, module, announcementTypes {
* @param extraData Data to give forward to the receiver
* @return Whether the transfer was successful or not
*/
function transfer(address to, uint256 amount, bytes extraData) isReady external returns (bool success) {
function transfer(address to, uint256 amount, bytes calldata extraData) isReady external returns (bool success) {
/*
Start transaction to send a quantity from a given address to another address
After transaction the function `receiveCorionToken`of the receiver is called by the given data

View File

@ -83,23 +83,23 @@ library strings {
uint ret;
if (self == 0)
return 0;
if (self & 0xffffffffffffffffffffffffffffffff == 0) {
if (uint256(self) & 0xffffffffffffffffffffffffffffffff == 0) {
ret += 16;
self = bytes32(uint(self) / 0x100000000000000000000000000000000);
}
if (self & 0xffffffffffffffff == 0) {
if (uint256(self) & 0xffffffffffffffff == 0) {
ret += 8;
self = bytes32(uint(self) / 0x10000000000000000);
}
if (self & 0xffffffff == 0) {
if (uint256(self) & 0xffffffff == 0) {
ret += 4;
self = bytes32(uint(self) / 0x100000000);
}
if (self & 0xffff == 0) {
if (uint256(self) & 0xffff == 0) {
ret += 2;
self = bytes32(uint(self) / 0x10000);
}
if (self & 0xff == 0) {
if (uint256(self) & 0xff == 0) {
ret += 1;
}
return 32 - ret;

View File

@ -55,7 +55,7 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
* @param _value The value to send
* @param _data The data part of the transaction
*/
function execute(address _to, uint256 _value, bytes _data) external onlyOwner returns (bytes32 _r) {
function execute(address _to, uint256 _value, bytes calldata _data) external onlyOwner returns (bytes32 _r) {
// first, take the opportunity to check that we're under the daily limit.
if (underLimit(_value)) {
emit SingleTransact(msg.sender, _value, _to, _data);

View File

@ -18,7 +18,7 @@ contract HasNoTokens is Ownable {
* @param value_ uint256 the amount of the specified token
* @param data_ Bytes The data passed from the caller.
*/
function tokenFallback(address from_, uint256 value_, bytes data_) external {
function tokenFallback(address from_, uint256 value_, bytes calldata data_) external {
revert();
}

View File

@ -23,6 +23,6 @@ contract Multisig {
// TODO: document
function changeOwner(address _from, address _to) external;
function execute(address _to, uint256 _value, bytes _data) external returns (bytes32);
function execute(address _to, uint256 _value, bytes calldata _data) external returns (bytes32);
function confirm(bytes32 _h) public returns (bool);
}

View File

@ -132,7 +132,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
}
}
function reserve(string _name) external payable {
function reserve(string calldata _name) external payable {
if (bytes(_name).length == 0)
revert();
bool needAuction = requiresAuction(_name);

View File

@ -348,7 +348,7 @@ contract multisig {
// TODO: document
function changeOwner(address _from, address _to) external;
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
function execute(address _to, uint _value, bytes calldata _data) external returns (bytes32);
function confirm(bytes32 _h) public returns (bool);
}
@ -390,7 +390,7 @@ contract Wallet is multisig, multiowned, daylimit {
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
// and _data arguments). They still get the option of using them if they want, anyways.
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
function execute(address _to, uint _value, bytes calldata _data) external onlyowner returns (bytes32 _r) {
// first, take the opportunity to check that we're under the daily limit.
if (underLimit(_value)) {
emit SingleTransact(msg.sender, _value, _to, _data);

View File

@ -48,6 +48,7 @@ BOOST_AUTO_TEST_CASE(test_similarity)
BOOST_CHECK_EQUAL(stringWithinDistance("abc", "abcdef", 2), false);
BOOST_CHECK_EQUAL(stringWithinDistance("abcd", "wxyz", 2), false);
BOOST_CHECK_EQUAL(stringWithinDistance("", "", 2), true);
BOOST_CHECK_EQUAL(stringWithinDistance("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZ", 2, 6400), false);
}
BOOST_AUTO_TEST_CASE(test_dldistance)

View File

@ -96,7 +96,7 @@ BOOST_AUTO_TEST_CASE(all_assembly_items)
_assembly.assemble().toHex(),
"5b6001600220606773__someLibrary___________________________"
"6000567f556e75736564206665617475726520666f722070757368696e"
"6720737472696e605f6001605e7300000000000000000000000000000000000000000000"
"6720737472696e605f6001605e73000000000000000000000000000000000000000000fe"
"fe010203044266eeaa"
);
BOOST_CHECK_EQUAL(

View File

@ -234,7 +234,7 @@ BOOST_AUTO_TEST_CASE(byte_arrays)
return (a, b.length, b[3], c);
}
function f_external(uint a, bytes b, uint c)
function f_external(uint a, bytes calldata b, uint c)
external pure returns (uint, uint, byte, uint) {
return (a, b.length, b[3], c);
}
@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(calldata_arrays_too_large)
{
string sourceCode = R"(
contract C {
function f(uint a, uint[] b, uint c) external pure returns (uint) {
function f(uint a, uint[] calldata b, uint c) external pure returns (uint) {
return 7;
}
}
@ -792,8 +792,8 @@ BOOST_AUTO_TEST_CASE(return_dynamic_types_cross_call_advanced)
a = "1234567890123456789012345678901234567890";
b = uint(-1);
c = new bytes20[](4);
c[0] = bytes20(1234);
c[3] = bytes20(6789);
c[0] = bytes20(uint160(1234));
c[3] = bytes20(uint160(6789));
d = 0x1234;
}
function f() public returns (bytes memory, uint, bytes20[] memory, uint) {

View File

@ -133,7 +133,7 @@ BOOST_AUTO_TEST_CASE(conversion)
int8 c;
int16 d;
assembly { a := sub(0, 1) c := 0x0101ff d := 0xff01 }
emit E(10, x, a, uint8(b), c, int8(d));
emit E(bytes4(uint32(10)), x, a, uint8(b), c, int8(d));
}
}
)";
@ -367,7 +367,7 @@ BOOST_AUTO_TEST_CASE(calldata)
string sourceCode = R"(
contract C {
event E(bytes);
function f(bytes a) external {
function f(bytes calldata a) external {
emit E(a);
}
}

View File

@ -83,7 +83,7 @@
"id" : 3,
"name" : "",
"nodeType" : "VariableDeclaration",
"scope" : 16,
"scope" : 5,
"src" : "61:4:1",
"stateVariable" : false,
"storageLocation" : "default",
@ -167,7 +167,7 @@
"id" : 10,
"name" : "",
"nodeType" : "VariableDeclaration",
"scope" : 16,
"scope" : 12,
"src" : "113:4:1",
"stateVariable" : false,
"storageLocation" : "default",

View File

@ -1 +1,3 @@
contract C { function f(function() external payable returns (uint) x) returns (function() external view returns (uint)) {} }
// ----

View File

@ -100,7 +100,7 @@
{
"constant" : false,
"name" : "",
"scope" : 16,
"scope" : 5,
"stateVariable" : false,
"storageLocation" : "default",
"type" : "uint256",
@ -191,7 +191,7 @@
{
"constant" : false,
"name" : "",
"scope" : 16,
"scope" : 12,
"stateVariable" : false,
"storageLocation" : "default",
"type" : "uint256",

View File

@ -122,7 +122,7 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs)
function f(uint a) public returns (uint b) {
x.length = a;
for (; a < 200; ++a) {
x[a] = 9;
x[a] = 0x09;
b = a * a;
}
return f(a - 1);

View File

@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(name_clash_in_import)
BOOST_AUTO_TEST_CASE(remappings)
{
CompilerStack c;
c.setRemappings(vector<string>{"s=s_1.4.6", "t=Tee"});
c.setRemappings(vector<CompilerStack::Remapping>{{"", "s", "s_1.4.6"},{"", "t", "Tee"}});
c.addSource("a", "import \"s/s.sol\"; contract A is S {} pragma solidity >=0.0;");
c.addSource("b", "import \"t/tee.sol\"; contract A is Tee {} pragma solidity >=0.0;");
c.addSource("s_1.4.6/s.sol", "contract S {} pragma solidity >=0.0;");
@ -179,7 +179,7 @@ BOOST_AUTO_TEST_CASE(remappings)
BOOST_AUTO_TEST_CASE(context_dependent_remappings)
{
CompilerStack c;
c.setRemappings(vector<string>{"a:s=s_1.4.6", "b:s=s_1.4.7"});
c.setRemappings(vector<CompilerStack::Remapping>{{"a", "s", "s_1.4.6"}, {"b", "s", "s_1.4.7"}});
c.addSource("a/a.sol", "import \"s/s.sol\"; contract A is SSix {} pragma solidity >=0.0;");
c.addSource("b/b.sol", "import \"s/s.sol\"; contract B is SSeven {} pragma solidity >=0.0;");
c.addSource("s_1.4.6/s.sol", "contract SSix {} pragma solidity >=0.0;");
@ -200,7 +200,11 @@ BOOST_AUTO_TEST_CASE(filename_with_period)
BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_preserved)
{
CompilerStack c;
c.setRemappings(vector<string>{"foo=vendor/foo_2.0.0", "vendor/bar:foo=vendor/foo_1.0.0", "bar=vendor/bar"});
c.setRemappings(vector<CompilerStack::Remapping>{
{"", "foo", "vendor/foo_2.0.0"},
{"vendor/bar", "foo", "vendor/foo_1.0.0"},
{"", "bar", "vendor/bar"}
});
c.addSource("main.sol", "import \"foo/foo.sol\"; import {Bar} from \"bar/bar.sol\"; contract Main is Foo2, Bar {} pragma solidity >=0.0;");
c.addSource("vendor/bar/bar.sol", "import \"foo/foo.sol\"; contract Bar {Foo1 foo;} pragma solidity >=0.0;");
c.addSource("vendor/foo_1.0.0/foo.sol", "contract Foo1 {} pragma solidity >=0.0;");
@ -212,7 +216,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_ensure_default_and_module_pres
BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent)
{
CompilerStack c;
c.setRemappings(vector<string>{"a:x/y/z=d", "a/b:x=e"});
c.setRemappings(vector<CompilerStack::Remapping>{{"a", "x/y/z", "d"}, {"a/b", "x", "e"}});
c.addSource("a/main.sol", "import \"x/y/z/z.sol\"; contract Main is D {} pragma solidity >=0.0;");
c.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;");
c.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;");
@ -220,7 +224,7 @@ BOOST_AUTO_TEST_CASE(context_dependent_remappings_order_independent)
c.setEVMVersion(dev::test::Options::get().evmVersion());
BOOST_CHECK(c.compile());
CompilerStack d;
d.setRemappings(vector<string>{"a/b:x=e", "a:x/y/z=d"});
d.setRemappings(vector<CompilerStack::Remapping>{{"a/b", "x", "e"}, {"a", "x/y/z", "d"}});
d.addSource("a/main.sol", "import \"x/y/z/z.sol\"; contract Main is D {} pragma solidity >=0.0;");
d.addSource("a/b/main.sol", "import \"x/y/z/z.sol\"; contract Main is E {} pragma solidity >=0.0;");
d.addSource("d/z.sol", "contract D {} pragma solidity >=0.0;");

View File

@ -111,12 +111,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["bytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
"6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00"
"6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfe"
);
BOOST_CHECK(contract["runtimeBytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
"6080604052600080fd00"
"6080604052600080fdfe"
);
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());
@ -153,12 +153,12 @@ BOOST_AUTO_TEST_CASE(single_compilation)
BOOST_CHECK(contract["bytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
"6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00"
"6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfe"
);
BOOST_CHECK(contract["runtimeBytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
"6080604052600080fd00"
"6080604052600080fdfe"
);
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());

View File

@ -727,7 +727,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
// bug #1801
char const* sourceCode = R"(
contract test {
function f(string a, bytes b, uint[] c) external {}
function f(string calldata a, bytes calldata b, uint[] calldata c) external {}
}
)";

File diff suppressed because it is too large Load Diff

View File

@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
uint a;
}
contract Test {
function boo(uint, bool, bytes8, bool[2], uint[], C, address[]) external returns (uint ret) {
function boo(uint, bool, bytes8, bool[2] calldata, uint[] calldata, C, address[] calldata) external returns (uint ret) {
ret = 5;
}
}
@ -206,10 +206,10 @@ BOOST_AUTO_TEST_CASE(external_structs)
struct Simple { uint i; }
struct Nested { X[2][] a; uint y; }
struct X { bytes32 x; Test t; Simple[] s; }
function f(ActionChoices, uint, Simple) external {}
function g(Test, Nested) external {}
function h(function(Nested memory) external returns (uint)[]) external {}
function i(Nested[]) external {}
function f(ActionChoices, uint, Simple calldata) external {}
function g(Test, Nested calldata) external {}
function h(function(Nested memory) external returns (uint)[] calldata) external {}
function i(Nested[] calldata) external {}
}
)";
SourceUnit const* sourceUnit = parseAndAnalyse(text);
@ -234,10 +234,10 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries)
struct Simple { uint i; }
struct Nested { X[2][] a; uint y; }
struct X { bytes32 x; Test t; Simple[] s; }
function f(ActionChoices, uint, Simple) external {}
function g(Test, Nested) external {}
function h(function(Nested memory) external returns (uint)[]) external {}
function i(Nested[]) external {}
function f(ActionChoices, uint, Simple calldata) external {}
function g(Test, Nested calldata) external {}
function h(function(Nested memory) external returns (uint)[] calldata) external {}
function i(Nested[] calldata) external {}
}
)";
SourceUnit const* sourceUnit = parseAndAnalyse(text);
@ -340,7 +340,7 @@ BOOST_AUTO_TEST_CASE(string)
char const* sourceCode = R"(
contract C {
string s;
function f(string x) external { s = x; }
function f(string calldata x) external { s = x; }
}
)";
BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));
@ -433,6 +433,37 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type)
}
}
BOOST_AUTO_TEST_CASE(address_staticcall)
{
char const* sourceCode = R"(
contract C {
function f() public view returns(bool) {
return address(0x4242).staticcall("");
}
}
)";
if (dev::test::Options::get().evmVersion().hasStaticCall())
CHECK_SUCCESS_NO_WARNINGS(sourceCode);
else
CHECK_ERROR(sourceCode, TypeError, "\"staticcall\" is not supported by the VM version.");
}
BOOST_AUTO_TEST_CASE(address_staticcall_value)
{
if (dev::test::Options::get().evmVersion().hasStaticCall())
{
char const* sourceCode = R"(
contract C {
function f() public view {
address(0x4242).staticcall.value;
}
}
)";
CHECK_ERROR(sourceCode, TypeError, "Member \"value\" not found or not visible after argument-dependent lookup");
}
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -683,6 +683,131 @@ BOOST_AUTO_TEST_CASE(dev_documenting_no_param_description)
expectNatspecError(sourceCode);
}
BOOST_AUTO_TEST_CASE(user_constructor)
{
char const *sourceCode = R"(
contract test {
/// @notice this is a really nice constructor
constructor(uint a, uint second) public { }
}
)";
char const *natspec = R"ABCDEF({
"methods" : {
"constructor" : "this is a really nice constructor"
}
})ABCDEF";
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_CASE(user_constructor_and_function)
{
char const *sourceCode = R"(
contract test {
/// @notice this is a really nice constructor
constructor(uint a, uint second) public { }
/// another multiplier
function mul(uint a, uint second) public returns(uint d) { return a * 7 + second; }
}
)";
char const *natspec = R"ABCDEF({
"methods" : {
"mul(uint256,uint256)" : {
"notice" : "another multiplier"
},
"constructor" : "this is a really nice constructor"
}
})ABCDEF";
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_CASE(dev_constructor)
{
char const *sourceCode = R"(
contract test {
/// @author Alex
/// @param a the parameter a is really nice and very useful
/// @param second the second parameter is not very useful, it just provides additional confusion
constructor(uint a, uint second) public { }
}
)";
char const *natspec = R"ABCDEF({
"methods" : {
"constructor" : {
"author" : "Alex",
"params" : {
"a" : "the parameter a is really nice and very useful",
"second" : "the second parameter is not very useful, it just provides additional confusion"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "test", natspec, false);
}
BOOST_AUTO_TEST_CASE(dev_constructor_return)
{
char const* sourceCode = R"(
contract test {
/// @author Alex
/// @param a the parameter a is really nice and very useful
/// @param second the second parameter is not very useful, it just provides additional confusion
/// @return return should not work within constructors
constructor(uint a, uint second) public { }
}
)";
expectNatspecError(sourceCode);
}
BOOST_AUTO_TEST_CASE(dev_constructor_and_function)
{
char const *sourceCode = R"(
contract test {
/// @author Alex
/// @param a the parameter a is really nice and very useful
/// @param second the second parameter is not very useful, it just provides additional confusion
constructor(uint a, uint second) public { }
/// @dev Multiplies a number by 7 and adds second parameter
/// @param a Documentation for the first parameter starts here.
/// Since it's a really complicated parameter we need 2 lines
/// @param second Documentation for the second parameter
/// @return The result of the multiplication
/// and cookies with nutella
function mul(uint a, uint second) public returns(uint d) {
return a * 7 + second;
}
}
)";
char const *natspec = R"ABCDEF({
"methods" : {
"mul(uint256,uint256)" : {
"details" : "Multiplies a number by 7 and adds second parameter",
"params" : {
"a" : "Documentation for the first parameter starts here. Since it's a really complicated parameter we need 2 lines",
"second" : "Documentation for the second parameter"
},
"return" : "The result of the multiplication and cookies with nutella"
},
"constructor" : {
"author" : "Alex",
"params" : {
"a" : "the parameter a is really nice and very useful",
"second" : "the second parameter is not very useful, it just provides additional confusion"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "test", natspec, false);
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
"6080604052348015600f57600080fd5b50603580601d6000396000f3006080604052600080fd00"
"6080604052348015600f57600080fd5b50603580601d6000396000f3fe6080604052600080fdfe"
);
BOOST_CHECK(contract["evm"]["assembly"].isString());
BOOST_CHECK(contract["evm"]["assembly"].asString().find(

View File

@ -53,8 +53,11 @@ BOOST_AUTO_TEST_CASE(environment_access)
"tx.origin",
"tx.gasprice",
"this",
"address(1).balance"
"address(1).balance",
};
if (dev::test::Options::get().evmVersion().hasStaticCall())
view.emplace_back("address(0x4242).staticcall(\"\")");
// ``block.blockhash`` and ``blockhash`` are tested separately below because their usage will
// produce warnings that can't be handled in a generic way.
vector<string> pure{
@ -95,6 +98,22 @@ BOOST_AUTO_TEST_CASE(environment_access)
);
}
BOOST_AUTO_TEST_CASE(address_staticcall)
{
string text = R"(
contract C {
function i() view public returns (bool) {
return address(0x4242).staticcall("");
}
}
)";
if (!dev::test::Options::get().evmVersion().hasStaticCall())
CHECK_ERROR(text, TypeError, "\"staticcall\" is not supported by the VM version.");
else
CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(assembly_staticcall)
{
string text = R"(

View File

@ -4,4 +4,5 @@ contract C {
}
}
// ----
// DeclarationError: (28-45): The "constant" keyword can only be used for state variables.
// TypeError: (69-72): Invalid array length, expected integer literal or constant expression.

View File

@ -0,0 +1,5 @@
contract C {
function f() internal pure returns (mapping(uint=>uint) storage r) { }
}
// ----
// TypeError: (53-82): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.

View File

@ -0,0 +1,5 @@
contract C {
mapping(uint=>uint) m;
function f() internal view returns (mapping(uint=>uint) storage r) { r = m; }
}
// ----

View File

@ -0,0 +1,5 @@
contract C {
function f() internal pure returns (mapping(uint=>uint) storage) {}
}
// ----
// TypeError: (53-72): This variable is of storage pointer type and might be returned without assignment and could be used uninitialized. Assign the variable (potentially from itself) to fix this error.

View File

@ -0,0 +1,5 @@
contract C {
mapping(uint=>uint) m;
function f() internal view returns (mapping(uint=>uint) storage) { return m; }
}
// ----

View File

@ -0,0 +1,5 @@
contract test {
function f() public pure returns (bytes memory) {
return bytes("abc");
}
}

View File

@ -0,0 +1,4 @@
library L {
struct Nested { uint y; }
function c(function(Nested memory) external returns (uint)[] storage) external pure {}
}

View File

@ -0,0 +1,9 @@
library L {
struct Nested { uint y; }
function b(function(Nested calldata) external returns (uint)[] storage) external pure {}
function d(function(Nested storage) external returns (uint)[] storage) external pure {}
}
// ----
// TypeError: (66-72): Data location must be "memory" for parameter in function, but "calldata" was given.
// TypeError: (159-165): Data location must be "memory" for parameter in function, but "storage" was given.

View File

@ -0,0 +1,5 @@
contract C {
function i() external pure returns(uint[]) {}
}
// ----
// TypeError: (52-58): Data location must be "memory" for return parameter in function, but none was given.

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes memory) external;
}
// ----
// TypeError: (31-36): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (31-36): Data location must be "calldata" for parameter in external function, but "memory" was given.

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes storage) external;
}
// ----
// TypeError: (31-36): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (31-36): Data location must be "calldata" for parameter in external function, but "storage" was given.

View File

@ -1,5 +0,0 @@
library test {
function f(bytes calldata) public;
}
// ----
// TypeError: (30-35): Location cannot be calldata for non-external functions (remove the "calldata" keyword).

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes4 memory) public;
}
// ----
// TypeError: (31-37): Data location can only be given for array or struct types.
// TypeError: (31-37): Data location can only be specified for array, struct or mapping types, but "memory" was given.

View File

@ -0,0 +1,8 @@
contract C {
function f(uint[] memory, uint[] storage) private pure {}
function g(uint[] memory, uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
function i(uint[] calldata) external pure {}
// No data location for events.
event e(uint[]);
}

Some files were not shown because too many files have changed in this diff Show More