diff options
96 files changed, 2455 insertions, 823 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..52031de5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sol linguist-language=Solidity diff --git a/.travis.yml b/.travis.yml index ebe91939..0b05f661 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,6 @@ env: global: - ENCRYPTION_LABEL="6d4541b72666" - SOLC_BUILD_TYPE=RelWithDebInfo - - SOLC_DOCS=Off - SOLC_EMSCRIPTEN=Off - SOLC_INSTALL_DEPS_TRAVIS=On - SOLC_RELEASE=On @@ -65,18 +64,6 @@ matrix: - ZIP_SUFFIX=ubuntu-trusty-clang - SOLC_STOREBYTECODE=On - # Documentation target, which generates documentation using Phoenix / ReadTheDocs. - - os: linux - dist: trusty - sudo: required - compiler: gcc - before_install: - - sudo apt-get -y install python-sphinx - env: - - SOLC_DOCS=On - - SOLC_RELEASE=Off - - SOLC_TESTS=Off - # Docker target, which generates a statically linked alpine image - os: linux dist: trusty @@ -184,7 +171,6 @@ before_script: script: - test $SOLC_EMSCRIPTEN != On || (scripts/test_emscripten.sh) - - test $SOLC_DOCS != On || (scripts/docs.sh) - test $SOLC_TESTS != On || (cd $TRAVIS_BUILD_DIR && scripts/tests.sh) - test $SOLC_STOREBYTECODE != On || (cd $TRAVIS_BUILD_DIR && scripts/bytecodecompare/storebytecode.sh) diff --git a/Changelog.md b/Changelog.md index ade9ac86..e027e8ad 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,10 +1,23 @@ ### 0.4.21 (unreleased) Features: - + * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature. + * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature. + * Standard JSON: Reject badly formatted invalid JSON inputs. + * Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature. + * Support and recommend using ``emit EventName();`` to call events explicitly. + * Syntax Analyser: Do not warn about experimental features if they do not concern code generation. + * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature. + * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature. Bugfixes: - + * Assembly: Raise error on oversized number literals in assembly. + * JSON-AST: Add "documentation" property to function, event and modifier definition. + * Resolver: Properly determine shadowing for imports with aliases. + * Standalone Assembly: Do not ignore input after closing brace of top level block. + * Standard JSON: catch errors properly when invalid "sources" are passed + * Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly. + * Commandline interface: throw error if option is unknown ### 0.4.20 (2018-02-14) @@ -83,7 +83,7 @@ jobs: name: External tests command: | . /usr/local/nvm/nvm.sh - test/externalTests.sh /tmp/workspace/soljson.js + test/externalTests.sh /tmp/workspace/soljson.js || test/externalTests.sh /tmp/workspace/soljson.js build_x86: docker: - image: buildpack-deps:artful @@ -101,7 +101,7 @@ jobs: - run: name: Store commit hash and prerelease command: | - date -u +"nightly.%Y.%-m.%-d" > prerelease.txt + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi echo -n "$CIRCLE_SHA1" > commit_hash.txt - restore_cache: key: ccache-{{ arch }}-{{ .Branch }} @@ -114,21 +114,56 @@ jobs: key: ccache-{{ arch }}-{{ .Branch }} paths: - ~/.ccache + - store_artifacts: + path: build/solc/solc + destination: solc + - persist_to_workspace: + root: build + paths: + - solc/solc + - test/soltest + - test/solfuzzer + + test_x86: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - attach_workspace: + at: build - run: - name: Commandline tests - command: test/cmdlineTests.sh + name: Install dependencies + command: | + apt-get -qq update + apt-get -qy install libz3-dev libleveldb1v5 - run: mkdir -p test_results - run: - name: Test without optimizer (exclude IPC tests) - command: build/test/soltest --logger=JUNIT,test_suite,test_results/no_opt.xml -- --no-ipc - - run: - name: Test with optimizer (exclude IPC tests) - command: build/test/soltest --logger=JUNIT,test_suite,test_results/opt.xml -- --optimize --no-ipc + name: Tests + command: scripts/tests.sh --junit_report test_results - store_test_results: path: test_results/ + + docs: + docker: + - image: buildpack-deps:artful + steps: + - checkout + - run: + name: Install build dependencies + command: | + apt-get -qq update + apt-get -qy install python-sphinx + - run: + name: Store commit hash and prerelease + command: | + if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi + echo -n "$CIRCLE_SHA1" > commit_hash.txt + - run: + name: Build documentation + command: ./scripts/docs.sh - store_artifacts: - path: build/solc/solc - destination: solc + path: docs/_build/html/ + destination: docs-html workflows: version: 2 @@ -142,3 +177,7 @@ workflows: requires: - build_emscripten - build_x86 + - test_x86: + requires: + - build_x86 + - docs diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index 7199f537..a9ed0a74 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -140,6 +140,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1") # Disable greedy exception catcher set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0") + # Abort if linking results in any undefined symbols + # Note: this is on by default in the CMake Emscripten module which we aren't using + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ERROR_ON_UNDEFINED_SYMBOLS=1") add_definitions(-DETH_EMSCRIPTEN=1) endif() endif() diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 8095a3b7..07c8e0ce 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -155,15 +155,15 @@ on the type of ``X`` being ``enc(X) = enc(enc_utf8(X))``, i.e. ``X`` is utf-8 encoded and this value is interpreted as of ``bytes`` type and encoded further. Note that the length used in this subsequent encoding is the number of bytes of the utf-8 encoded string, not its number of characters. -- ``uint<M>``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is a multiple of 32 bytes. +- ``uint<M>``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order (left) side with zero-bytes such that the length is 32 bytes. - ``address``: as in the ``uint160`` case -- ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-oder (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is a multiple of 32 bytes. +- ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes. - ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``fixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``. - ``fixed``: as in the ``fixed128x19`` case - ``ufixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``. - ``ufixed``: as in the ``ufixed128x19`` case -- ``bytes<M>``: ``enc(X)`` is the sequence of bytes in ``X`` padded with zero-bytes to a length of 32. +- ``bytes<M>``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes. Note that for any ``X``, ``len(enc(X))`` is a multiple of 32. diff --git a/docs/assembly.rst b/docs/assembly.rst index afc44d66..cf9bf840 100644 --- a/docs/assembly.rst +++ b/docs/assembly.rst @@ -153,6 +153,8 @@ If an opcode takes arguments (always from the top of the stack), they are given Note that the order of arguments can be seen to be reversed in non-functional style (explained below). Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are special and all others push exactly one item onto the stack. +Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively. +Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception. In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to (excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``. @@ -161,165 +163,173 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly. In the grammar, opcodes are represented as pre-defined identifiers. -+-------------------------+------+-----------------------------------------------------------------+ -| stop + `-` | stop execution, identical to return(0,0) | -+-------------------------+------+-----------------------------------------------------------------+ -| add(x, y) | | x + y | -+-------------------------+------+-----------------------------------------------------------------+ -| sub(x, y) | | x - y | -+-------------------------+------+-----------------------------------------------------------------+ -| mul(x, y) | | x * y | -+-------------------------+------+-----------------------------------------------------------------+ -| div(x, y) | | x / y | -+-------------------------+------+-----------------------------------------------------------------+ -| sdiv(x, y) | | x / y, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| mod(x, y) | | x % y | -+-------------------------+------+-----------------------------------------------------------------+ -| smod(x, y) | | x % y, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| exp(x, y) | | x to the power of y | -+-------------------------+------+-----------------------------------------------------------------+ -| not(x) | | ~x, every bit of x is negated | -+-------------------------+------+-----------------------------------------------------------------+ -| lt(x, y) | | 1 if x < y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| gt(x, y) | | 1 if x > y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement | -+-------------------------+------+-----------------------------------------------------------------+ -| eq(x, y) | | 1 if x == y, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| iszero(x) | | 1 if x == 0, 0 otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| and(x, y) | | bitwise and of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| or(x, y) | | bitwise or of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| xor(x, y) | | bitwise xor of x and y | -+-------------------------+------+-----------------------------------------------------------------+ -| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte | -+-------------------------+------+-----------------------------------------------------------------+ -| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics | -+-------------------------+------+-----------------------------------------------------------------+ -| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics | -+-------------------------+------+-----------------------------------------------------------------+ -| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant | -+-------------------------+------+-----------------------------------------------------------------+ -| keccak256(p, n) | | keccak(mem[p...(p+n))) | -+-------------------------+------+-----------------------------------------------------------------+ -| sha3(p, n) | | keccak(mem[p...(p+n))) | -+-------------------------+------+-----------------------------------------------------------------+ -| jump(label) | `-` | jump to label / code position | -+-------------------------+------+-----------------------------------------------------------------+ -| jumpi(label, cond) | `-` | jump to label if cond is nonzero | -+-------------------------+------+-----------------------------------------------------------------+ -| pc | | current position in code | -+-------------------------+------+-----------------------------------------------------------------+ -| pop(x) | `-` | remove the element pushed by x | -+-------------------------+------+-----------------------------------------------------------------+ -| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) | -+-------------------------+------+-----------------------------------------------------------------+ -| swap1 ... swap16 | `*` | swap topmost and ith stack slot below it | -+-------------------------+------+-----------------------------------------------------------------+ -| mload(p) | | mem[p..(p+32)) | -+-------------------------+------+-----------------------------------------------------------------+ -| mstore(p, v) | `-` | mem[p..(p+32)) := v | -+-------------------------+------+-----------------------------------------------------------------+ -| mstore8(p, v) | `-` | mem[p] := v & 0xff - only modifies a single byte | -+-------------------------+------+-----------------------------------------------------------------+ -| sload(p) | | storage[p] | -+-------------------------+------+-----------------------------------------------------------------+ -| sstore(p, v) | `-` | storage[p] := v | -+-------------------------+------+-----------------------------------------------------------------+ -| msize | | size of memory, i.e. largest accessed memory index | -+-------------------------+------+-----------------------------------------------------------------+ -| gas | | gas still available to execution | -+-------------------------+------+-----------------------------------------------------------------+ -| address | | address of the current contract / execution context | -+-------------------------+------+-----------------------------------------------------------------+ -| balance(a) | | wei balance at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| caller | | call sender (excluding delegatecall) | -+-------------------------+------+-----------------------------------------------------------------+ -| callvalue | | wei sent together with the current call | -+-------------------------+------+-----------------------------------------------------------------+ -| calldataload(p) | | call data starting from position p (32 bytes) | -+-------------------------+------+-----------------------------------------------------------------+ -| calldatasize | | size of call data in bytes | -+-------------------------+------+-----------------------------------------------------------------+ -| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| codesize | | size of the code of the current contract / execution context | -+-------------------------+------+-----------------------------------------------------------------+ -| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| extcodesize(a) | | size of the code at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a | -+-------------------------+------+-----------------------------------------------------------------+ -| returndatasize | | size of the last returndata | -+-------------------------+------+-----------------------------------------------------------------+ -| returndatacopy(t, f, s) | `-` | copy s bytes from returndata at position f to mem at position t | -+-------------------------+------+-----------------------------------------------------------------+ -| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei | -| | | and return the new address | -+-------------------------+------+-----------------------------------------------------------------+ -| create2(v, n, p, s) | | create new contract with code mem[p..(p+s)) at address | -| | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v | -| | | wei and return the new address | -+-------------------------+------+-----------------------------------------------------------------+ -| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) | -| insize, out, outsize) | | providing g gas and v wei and output area | -| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | -| | | and 1 on success | -+-------------------------+------+-----------------------------------------------------------------+ -| callcode(g, a, v, in, | | identical to `call` but only use the code from a and stay | -| insize, out, outsize) | | in the context of the current contract otherwise | -+-------------------------+------+-----------------------------------------------------------------+ -| delegatecall(g, a, in, | | identical to `callcode` but also keep ``caller`` | -| insize, out, outsize) | | and ``callvalue`` | -+-------------------------+------+-----------------------------------------------------------------+ -| staticcall(g, a, in, | | identical to `call(g, a, 0, in, insize, out, outsize)` but do | -| insize, out, outsize) | | not allow state modifications | -+-------------------------+------+-----------------------------------------------------------------+ -| return(p, s) | `-` | end execution, return data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a | -+-------------------------+------+-----------------------------------------------------------------+ -| invalid | `-` | end execution with invalid instruction | -+-------------------------+------+-----------------------------------------------------------------+ -| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) | -+-------------------------+------+-----------------------------------------------------------------+ -| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | -| t4) | | | -+-------------------------+------+-----------------------------------------------------------------+ -| origin | | transaction sender | -+-------------------------+------+-----------------------------------------------------------------+ -| gasprice | | gas price of the transaction | -+-------------------------+------+-----------------------------------------------------------------+ -| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current | -+-------------------------+------+-----------------------------------------------------------------+ -| coinbase | | current mining beneficiary | -+-------------------------+------+-----------------------------------------------------------------+ -| timestamp | | timestamp of the current block in seconds since the epoch | -+-------------------------+------+-----------------------------------------------------------------+ -| number | | current block number | -+-------------------------+------+-----------------------------------------------------------------+ -| difficulty | | difficulty of the current block | -+-------------------------+------+-----------------------------------------------------------------+ -| gaslimit | | block gas limit of the current block | -+-------------------------+------+-----------------------------------------------------------------+ ++-------------------------+-----+---+-----------------------------------------------------------------+ +| Instruction | | | Explanation | ++=========================+=====+===+=================================================================+ +| stop + `-` | F | stop execution, identical to return(0,0) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| add(x, y) | | F | x + y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sub(x, y) | | F | x - y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mul(x, y) | | F | x * y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| div(x, y) | | F | x / y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sdiv(x, y) | | F | x / y, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mod(x, y) | | F | x % y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| smod(x, y) | | F | x % y, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| exp(x, y) | | F | x to the power of y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| not(x) | | F | ~x, every bit of x is negated | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| lt(x, y) | | F | 1 if x < y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gt(x, y) | | F | 1 if x > y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| eq(x, y) | | F | 1 if x == y, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| iszero(x) | | F | 1 if x == 0, 0 otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| and(x, y) | | F | bitwise and of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| or(x, y) | | F | bitwise or of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| xor(x, y) | | F | bitwise xor of x and y | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| shl(x, y) | | C | logical shift left y by x bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| shr(x, y) | | C | logical shift right y by x bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sar(x, y) | | C | arithmetic shift right y by x bits | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| keccak256(p, n) | | F | keccak(mem[p...(p+n))) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sha3(p, n) | | F | keccak(mem[p...(p+n))) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| jump(label) | `-` | F | jump to label / code position | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| jumpi(label, cond) | `-` | F | jump to label if cond is nonzero | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| pc | | F | current position in code | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| pop(x) | `-` | F | remove the element pushed by x | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mload(p) | | F | mem[p..(p+32)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mstore(p, v) | `-` | F | mem[p..(p+32)) := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sload(p) | | F | storage[p] | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| sstore(p, v) | `-` | F | storage[p] := v | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| msize | | F | size of memory, i.e. largest accessed memory index | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gas | | F | gas still available to execution | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| address | | F | address of the current contract / execution context | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| balance(a) | | F | wei balance at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| caller | | F | call sender (excluding ``delegatecall``) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| callvalue | | F | wei sent together with the current call | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldataload(p) | | F | call data starting from position p (32 bytes) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldatasize | | F | size of call data in bytes | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| codesize | | F | size of the code of the current contract / execution context | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodesize(a) | | F | size of the code at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| returndatasize | | B | size of the last returndata | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei | +| | | | and return the new address | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address | +| | | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v | +| | | | wei and return the new address | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) | +| insize, out, outsize) | | | providing g gas and v wei and output area | +| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | +| | | | and 1 on success | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay | +| insize, out, outsize) | | | in the context of the current contract otherwise | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` | +| insize, out, outsize) | | | and ``callvalue`` | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do | +| insize, out, outsize) | | | not allow state modifications | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| invalid | `-` | F | end execution with invalid instruction | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | +| t4) | | | | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| origin | | F | transaction sender | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gasprice | | F | gas price of the transaction | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| coinbase | | F | current mining beneficiary | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| timestamp | | F | timestamp of the current block in seconds since the epoch | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| number | | F | current block number | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| difficulty | | F | difficulty of the current block | ++-------------------------+-----+---+-----------------------------------------------------------------+ +| gaslimit | | F | block gas limit of the current block | ++-------------------------+-----+---+-----------------------------------------------------------------+ Literals -------- @@ -376,8 +386,8 @@ 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 + - 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. @@ -727,7 +737,7 @@ The following assembly will be generated:: // function dispatcher switch div(calldataload(0), exp(2, 226)) case 0xb3de648b { - let (r) = f(calldataload(4)) + let r := f(calldataload(4)) let ret := $allocate(0x20) mstore(ret, r) return(ret, 0x20) @@ -861,38 +871,37 @@ Grammar:: AssemblyItem = Identifier | AssemblyBlock | - FunctionalAssemblyExpression | + AssemblyExpression | AssemblyLocalDefinition | - FunctionalAssemblyAssignment | AssemblyAssignment | + AssemblyStackAssignment | LabelDefinition | AssemblyIf | AssemblySwitch | AssemblyFunctionDefinition | AssemblyFor | - 'break' | 'continue' | - SubAssembly | 'dataSize' '(' Identifier ')' | - LinkerSymbol | - 'errorLabel' | 'bytecodeSize' | - NumberLiteral | StringLiteral | HexLiteral + 'break' | + 'continue' | + SubAssembly + AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral + AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral Identifier = [a-zA-Z_$] [a-zA-Z_0-9]* - FunctionalAssemblyExpression = Identifier '(' ( AssemblyItem ( ',' AssemblyItem )* )? ')' - AssemblyLocalDefinition = 'let' IdentifierOrList ':=' FunctionalAssemblyExpression - FunctionalAssemblyAssignment = IdentifierOrList ':=' FunctionalAssemblyExpression + AssemblyCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')' + AssemblyLocalDefinition = 'let' IdentifierOrList ( ':=' AssemblyExpression )? + AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression IdentifierOrList = Identifier | '(' IdentifierList ')' IdentifierList = Identifier ( ',' Identifier)* - AssemblyAssignment = '=:' Identifier + AssemblyStackAssignment = '=:' Identifier LabelDefinition = Identifier ':' - AssemblyIf = 'if' FunctionalAssemblyExpression AssemblyBlock - AssemblySwitch = 'switch' FunctionalAssemblyExpression AssemblyCase* + AssemblyIf = 'if' AssemblyExpression AssemblyBlock + AssemblySwitch = 'switch' AssemblyExpression AssemblyCase* ( 'default' AssemblyBlock )? - AssemblyCase = 'case' FunctionalAssemblyExpression AssemblyBlock + AssemblyCase = 'case' AssemblyExpression AssemblyBlock AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')' ( '->' '(' IdentifierList ')' )? AssemblyBlock - AssemblyFor = 'for' ( AssemblyBlock | FunctionalAssemblyExpression) - FunctionalAssemblyExpression ( AssemblyBlock | FunctionalAssemblyExpression) AssemblyBlock + AssemblyFor = 'for' ( AssemblyBlock | AssemblyExpression ) + AssemblyExpression ( AssemblyBlock | AssemblyExpression ) AssemblyBlock SubAssembly = 'assembly' Identifier AssemblyBlock - LinkerSymbol = 'linkerSymbol' '(' StringLiteral ')' NumberLiteral = HexNumber | DecimalNumber HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' diff --git a/docs/contracts.rst b/docs/contracts.rst index afc32b16..416dc649 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -467,13 +467,13 @@ The following statements are considered modifying the state: } .. note:: - ``constant`` is an alias to ``view``. + ``constant`` on functions is an alias to ``view``, but this is deprecated and is planned to be dropped in version 0.5.0. .. note:: Getter methods are marked ``view``. .. warning:: - The compiler does not enforce yet that a ``view`` method is not modifying state. + Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state. .. index:: ! pure function, function;pure @@ -503,7 +503,7 @@ In addition to the list of state modifying statements explained above, the follo } .. warning:: - The compiler does not enforce yet that a ``pure`` method is not reading from the state. + Before version 0.4.17 the compiler didn't enforce that ``view`` is not reading the state. .. index:: ! fallback function, function;fallback @@ -724,10 +724,12 @@ All non-indexed arguments will be stored in the data part of the log. ); function deposit(bytes32 _id) public payable { - // Any call to this function (even deeply nested) can - // be detected from the JavaScript API by filtering - // for `Deposit` to be called. - Deposit(msg.sender, _id, msg.value); + // Events are emitted using `emit`, followed by + // the name of the event and the arguments + // (if any) in parentheses. Any such invocation + // (even deeply nested) can be detected from + // the JavaScript API by filtering for `Deposit`. + emit Deposit(msg.sender, _id, msg.value); } } @@ -1053,6 +1055,17 @@ but they can be used as base contracts:: If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. +Note that a function without implementation is different from a :ref:`Function Type <function_types>` even though their syntax looks very similar. + +Example of function without implementation (a function declaration):: + + function foo(address) external returns (address); + +Example of a Function Type (a variable declaration, where the variable is of type ``function``):: + + function(address) external returns (address) foo; + + .. index:: ! contract;interface, ! interface contract ********** diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 7be92cfa..46e076e5 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -324,7 +324,8 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes`` and ``string``, the default value is an empty array or string. -A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared. +A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared +(this will change soon, see below). This happens because Solidity inherits its scoping rules from JavaScript. This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block. As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``:: @@ -366,7 +367,9 @@ As a result, the following code is illegal and cause the compiler to throw an er } In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value. -As a result, the following code is legal, despite being poorly written:: +As a result, the following code is legal, despite being poorly written: + +:: pragma solidity ^0.4.0; @@ -383,6 +386,60 @@ As a result, the following code is legal, despite being poorly written:: } } +Scoping starting from Version 0.5.0 +----------------------------------- + +Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99 +(and many other languages): Variables are visible from the point right after their declaration +until the end of a ``{ }``-block. As an exception to this rule, variables declared in the +initialization part of a for-loop are only visible until the end of the for-loop. + +Variables and other items declared outside of a code block, for example functions, contracts, +user-defined types, etc., do not change their scoping behaviour. This means you can +use state variables before they are declared and call functions recursively. + +These rules are already introduced now as an experimental feature. + +As a consequence, the following examples will compile without warnings, since +the two variables have the same name but disjoint scopes. In non-0.5.0-mode, +they have the same scope (the function ``minimalScoping``) and thus it does +not compile there. + +:: + + pragma solidity ^0.4.0; + pragma experimental "v0.5.0"; + contract C { + function minimalScoping() pure public { + { + uint same2 = 0; + } + + { + uint same2 = 0; + } + } + } + +As a special example of the C99 scoping rules, note that in the following, +the first assignment to ``x`` will actually assign the outer and not the inner variable. +In any case, you will get a warning about the outer variable being shadowed. + +:: + + pragma solidity ^0.4.0; + pragma experimental "v0.5.0"; + contract C { + function f() pure public returns (uint) { + uint x = 1; + { + x = 2; // this will assign to the outer variable + uint x; + } + return x; // x has value 2 + } + } + .. index:: ! exception, ! throw, ! assert, ! require, ! revert Error handling: Assert, Require, Revert and Exceptions diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index a6bead29..6a2fe685 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -9,17 +9,6 @@ This list was originally compiled by `fivedogit <mailto:fivedogit@gmail.com>`_. Basic Questions *************** -Example contracts -================= - -There are some `contract examples <https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts/>`_ by fivedogit and -there should be a `test contract <https://github.com/ethereum/solidity/blob/develop/test/libsolidity/SolidityEndToEndTest.cpp>`_ for every single feature of Solidity. - -Create and publish the most basic contract possible -=================================================== - -A quite simple contract is the `greeter <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol>`_ - Is it possible to do something on a specific block number? (e.g. publish a contract or execute a transaction) ============================================================================================================= @@ -40,9 +29,9 @@ Is there a decompiler available? ================================ There is no exact decompiler to Solidity, but -`Porosity <https://github.com/comaeio/porosity>`_ is close. -Because some information like variable names, comments, and -source code formatting is lost in the compilation process, +`Porosity <https://github.com/comaeio/porosity>`_ is close. +Because some information like variable names, comments, and +source code formatting is lost in the compilation process, it is not possible to completely recover the original source code. Bytecode can be disassembled to opcodes, a service that is provided by @@ -74,25 +63,6 @@ has it (which includes `Remix <https://remix.ethereum.org/>`_), then ``contractname.kill.sendTransaction({from:eth.coinbase})``, just the same as my examples. -Store Ether in a contract -========================= - -The trick is to create the contract with ``{from:someaddress, value: web3.toWei(3,"ether")...}`` - -See `endowment_retriever.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_. - -Use a non-constant function (req ``sendTransaction``) to increment a variable in a contract -=========================================================================================== - -See `value_incrementer.sol <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/20_value_incrementer.sol>`_. - -Get a contract to return its funds to you (not using ``selfdestruct(...)``). -============================================================================ - -This example demonstrates how to send funds from a contract to an address. - -See `endowment_retriever <https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/30_endowment_retriever.sol>`_. - Can you return an array or a ``string`` from a solidity function call? ====================================================================== @@ -542,12 +512,27 @@ contract level) with ``arrayname.length = <some new length>;``. If you get the :: - int8[] memory memArr; // Case 1 - memArr.length++; // illegal - int8[5] storageArr; // Case 2 - somearray.length++; // legal - int8[5] storage storageArr2; // Explicit case 2 - somearray2.length++; // legal + // This will not compile + + pragma solidity ^0.4.18; + + contract C { + int8[] dynamicStorageArray; + int8[5] fixedStorageArray; + + function f() { + int8[] memory memArr; // Case 1 + memArr.length++; // illegal + + int8[5] storage storageArr = fixedStorageArray; // Case 2 + storageArr.length++; // illegal + + int8[] storage storageArr2 = dynamicStorageArray; + storageArr2.length++; // legal + + + } + } **Important note:** In Solidity, array dimensions are declared backwards from the way you might be used to declaring them in C or Java, but they are access as in diff --git a/docs/grammar.txt b/docs/grammar.txt index e700c946..a5c2acf3 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -63,7 +63,7 @@ StateMutability = 'pure' | 'constant' | 'view' | 'payable' Block = '{' Statement* '}' Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | SimpleStatement ) ';' + Throw | EmitStatement | SimpleStatement ) ';' ExpressionStatement = Expression IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? @@ -77,6 +77,7 @@ Continue = 'continue' Break = 'break' Return = 'return' Expression? Throw = 'throw' +EmitStatement = 'emit' FunctionCall VariableDefinition = ('var' IdentifierList | VariableDeclaration) ( '=' Expression )? IdentifierList = '(' ( Identifier? ',' )* Identifier? ')' diff --git a/docs/index.rst b/docs/index.rst index 3df0af3c..184d0e69 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -34,6 +34,7 @@ Translations This documentation is translated into several languages by community volunteers, but the English version stands as a reference. +* `Simplified Chinese <http://solidity-cn.readthedocs.io>`_ (in progress) * `Spanish <https://solidity-es.readthedocs.io>`_ * `Russian <https://github.com/ethereum/wiki/wiki/%5BRussian%5D-%D0%A0%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE-%D0%BF%D0%BE-Solidity>`_ (rather outdated) diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index 8f30f199..e26870f0 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -29,18 +29,20 @@ Further options on this page detail installing commandline Solidity compiler sof on your computer. Choose a commandline compiler if you are working on a larger contract or if you require more compilation options. +.. _solcjs: + npm / Node.js ============= Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The `solcjs` program has less features than all options further down this page. Our -`Using the compiler <using-the-compiler.html>` documentation assumes you are using +:ref:`commandline-compiler` documentation assumes you are using the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will -stop reading the documentation here and then continue to <https://github.com/ethereum/solc-js>, +stop reading the documentation here and then continue to `solc-js <https://github.com/ethereum/solc-js>`_. -Note: The `solc-js <https://github.com/ethereum/solc-js>` project is derived from the C++ +Note: The solc-js project is derived from the C++ `solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix). -Please refer to the `solc-js <https://github.com/ethereum/solc-js>`_ repository for instructions. +Please refer to the solc-js repository for instructions. .. code:: bash diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst index 6425dcaa..11e07292 100644 --- a/docs/introduction-to-smart-contracts.rst +++ b/docs/introduction-to-smart-contracts.rst @@ -80,7 +80,7 @@ registering with username and password - all you need is an Ethereum keypair. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Coin { // The keyword "public" makes those variables @@ -107,7 +107,7 @@ registering with username and password - all you need is an Ethereum keypair. if (balances[msg.sender] < amount) return; balances[msg.sender] -= amount; balances[receiver] += amount; - Sent(msg.sender, receiver, amount); + emit Sent(msg.sender, receiver, amount); } } @@ -118,9 +118,11 @@ that is publicly accessible. The ``address`` type is a 160-bit value that does not allow any arithmetic operations. It is suitable for storing addresses of contracts or keypairs belonging to external persons. The keyword ``public`` automatically generates a function that -allows you to access the current value of the state variable. +allows you to access the current value of the state variable +from outside of the contract. Without this keyword, other contracts have no way to access the variable. -The function will look something like this:: +The code of the function generated by the compiler is roughly equivalent +to the following:: function minter() returns (address) { return minter; } @@ -155,10 +157,10 @@ single account. .. index:: event The line ``event Sent(address from, address to, uint amount);`` declares -a so-called "event" which is fired in the last line of the function +a so-called "event" which is emitted in the last line of the function ``send``. User interfaces (as well as server applications of course) can -listen for those events being fired on the blockchain without much -cost. As soon as it is fired, the listener will also receive the +listen for those events being emitted on the blockchain without much +cost. As soon as it is emitted, the listener will also receive the arguments ``from``, ``to`` and ``amount``, which makes it easy to track transactions. In order to listen for this event, you would use :: diff --git a/docs/julia.rst b/docs/julia.rst index 9e961a9d..078bc55b 100644 --- a/docs/julia.rst +++ b/docs/julia.rst @@ -320,168 +320,169 @@ The following functions must be available: +---------------------------------------------------------------------------------------------------------------+ | *Arithmetics* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | addu256(x:u256, y:u256) -> z:u256 | x + y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | subu256(x:u256, y:u256) -> z:u256 | x - y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mulu256(x:u256, y:u256) -> z:u256 | x * y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | divu256(x:u256, y:u256) -> z:u256 | x / y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | modu256(x:u256, y:u256) -> z:u256 | x % y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | expu256(x:u256, y:u256) -> z:u256 | x to the power of y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | notu256(x:u256) -> z:u256 | ~x, every bit of x is negated | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte | -| Cannot this be just replaced by and256(shr256(n, x), 0xff) and let it be optimised out by the EVM backend? | -+---------------------------------------------------------------------------------------------------------------+ +| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and | +| | let it be optimised out by the EVM backend? | ++---------------------------------------------+-----------------------------------------------------------------+ | *Memory and storage* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mload(p:u256) -> v:u256 | mem[p..(p+32)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mstore(p:u256, v:u256) | mem[p..(p+32)) := v | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sload(p:u256) -> v:u256 | storage[p] | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sstore(p:u256, v:u256) | storage[p] := v | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due | | | due to the memory extension function, which extends by words, | | | this will always be a multiple of 32 bytes | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *Execution control* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei | | | and return the new address | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) | | insize:u256, out:u256, | providing g gas and v wei and output area | | outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) | | -> r:u256 | and 1 on success | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a | | insize:u256, out:u256, | and stay in the context of the | | outsize:u256) -> r:u256 | current contract otherwise | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, | | insize:u256, out:u256, | but also keep ``caller`` | | outsize:u256) -> r:u256 | and ``callvalue`` | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | stop() | stop execution, identical to return(0,0) | -| Perhaps it would make sense retiring this as it equals to return(0,0). It can be an optimisation by the EVM | -| backend. | -+---------------------------------------------------------------------------------------------------------------+ +| | Perhaps it would make sense retiring this as it equals to | +| | return(0,0). It can be an optimisation by the EVM backend. | ++---------------------------------------------+-----------------------------------------------------------------+ | abort() | abort (equals to invalid instruction on EVM) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | selfdestruct(a:u256) | end execution, destroy current contract and send funds to a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) | | t3:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | | t3:u256, t4:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *State queries* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockcoinbase() -> address:u256 | current mining beneficiary | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockdifficulty() -> difficulty:u256 | difficulty of the current block | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockgaslimit() -> limit:u256 | block gas limit of the current block | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blocknumber() -> block:u256 | current block number | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | txorigin() -> address:u256 | transaction sender | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | txgasprice() -> price:u256 | gas price of the transaction | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | gasleft() -> gas:u256 | gas still available to execution | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | balance(a:u256) -> v:u256 | wei balance at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | this() -> address:u256 | address of the current contract / execution context | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | caller() -> address:u256 | call sender (excluding delegatecall) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | callvalue() -> v:u256 | wei sent together with the current call | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldatasize() -> v:u256 | size of call data in bytes | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | codesize() -> size:u256 | size of the code of the current contract / execution context | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | extcodesize(a:u256) -> size:u256 | size of the code at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | *Others* | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | discardu256(unused:u256) | discard value | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's | | x3:u64, x4:u64) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 | | x4:u64) -> (x:u256) | | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ | sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) | -+---------------------------------------------------------------------------------------------------------------+ ++---------------------------------------------+-----------------------------------------------------------------+ Backends -------- diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 1c4f918c..b5d605ac 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -219,7 +219,7 @@ This means the following source mappings represent the same information: ``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2`` -``1:2:1;:9;2::2;;`` +``1:2:1;:9;2:1:2;;`` *************** Tips and Tricks @@ -327,8 +327,8 @@ Global Variables - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` - ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` - ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature, return zero on error -- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256`` -- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256`` +- ``addmod(uint x, uint y, uint k) returns (uint)``: compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. +- ``mulmod(uint x, uint y, uint k) returns (uint)``: compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` - ``super``: the contract one level higher in the inheritance hierarchy - ``selfdestruct(address recipient)``: destroy the current contract, sending its funds to the given address diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst index b663083c..57556fa5 100644 --- a/docs/solidity-by-example.rst +++ b/docs/solidity-by-example.rst @@ -94,7 +94,11 @@ of votes. // called incorrectly. But watch out, this // will currently also consume all provided gas // (this is planned to change in the future). - require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0)); + require( + (msg.sender == chairperson) && + !voters[voter].voted && + (voters[voter].weight == 0) + ); voters[voter].weight = 1; } @@ -126,15 +130,15 @@ of votes. // modifies `voters[msg.sender].voted` sender.voted = true; sender.delegate = to; - Voter storage delegate = voters[to]; - if (delegate.voted) { + Voter storage delegate_ = voters[to]; + if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes - proposals[delegate.vote].voteCount += sender.weight; + proposals[delegate_.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. - delegate.weight += sender.weight; + delegate_.weight += sender.weight; } } @@ -155,13 +159,13 @@ of votes. /// @dev Computes the winning proposal taking all /// previous votes into account. function winningProposal() public view - returns (uint winningProposal) + returns (uint winningProposal_) { uint winningVoteCount = 0; for (uint p = 0; p < proposals.length; p++) { if (proposals[p].voteCount > winningVoteCount) { winningVoteCount = proposals[p].voteCount; - winningProposal = p; + winningProposal_ = p; } } } @@ -170,12 +174,13 @@ of votes. // of the winner contained in the proposals array and then // returns the name of the winner function winnerName() public view - returns (bytes32 winnerName) + returns (bytes32 winnerName_) { - winnerName = proposals[winningProposal()].name; + winnerName_ = proposals[winningProposal()].name; } } + Possible Improvements ===================== @@ -214,7 +219,7 @@ activate themselves. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract SimpleAuction { // Parameters of the auction. Times are either @@ -272,7 +277,7 @@ activate themselves. // money back. require(msg.value > highestBid); - if (highestBidder != 0) { + if (highestBid != 0) { // Sending back the money by simply using // highestBidder.send(highestBid) is a security risk // because it could execute an untrusted contract. @@ -282,7 +287,7 @@ activate themselves. } highestBidder = msg.sender; highestBid = msg.value; - HighestBidIncreased(msg.sender, msg.value); + emit HighestBidIncreased(msg.sender, msg.value); } /// Withdraw a bid that was overbid. @@ -325,7 +330,7 @@ activate themselves. // 2. Effects ended = true; - AuctionEnded(highestBidder, highestBid); + emit AuctionEnded(highestBidder, highestBid); // 3. Interaction beneficiary.transfer(highestBid); @@ -371,7 +376,7 @@ high or low invalid bids. :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract BlindAuction { struct Bid { @@ -509,7 +514,7 @@ high or low invalid bids. onlyAfter(revealEnd) { require(!ended); - AuctionEnded(highestBidder, highestBid); + emit AuctionEnded(highestBidder, highestBid); ended = true; beneficiary.transfer(highestBid); } @@ -524,7 +529,7 @@ Safe Remote Purchase :: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Purchase { uint public value; @@ -574,7 +579,7 @@ Safe Remote Purchase onlySeller inState(State.Created) { - Aborted(); + emit Aborted(); state = State.Inactive; seller.transfer(this.balance); } @@ -589,7 +594,7 @@ Safe Remote Purchase condition(msg.value == (2 * value)) payable { - PurchaseConfirmed(); + emit PurchaseConfirmed(); buyer = msg.sender; state = State.Locked; } @@ -601,7 +606,7 @@ Safe Remote Purchase onlyBuyer inState(State.Locked) { - ItemReceived(); + emit ItemReceived(); // It is important to change the state first because // otherwise, the contracts called using `send` below // can call in again here. diff --git a/docs/structure-of-a-contract.rst b/docs/structure-of-a-contract.rst index a9a7ed52..4a0873df 100644 --- a/docs/structure-of-a-contract.rst +++ b/docs/structure-of-a-contract.rst @@ -86,14 +86,14 @@ Events are convenience interfaces with the EVM logging facilities. :: - pragma solidity ^0.4.0; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract SimpleAuction { event HighestBidIncreased(address bidder, uint amount); // Event function bid() public payable { // ... - HighestBidIncreased(msg.sender, msg.value); // Triggering event + emit HighestBidIncreased(msg.sender, msg.value); // Triggering event } } diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 533c4be5..2261746f 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -847,7 +847,7 @@ naming styles. * ``mixedCase`` (differs from CapitalizedWords by initial lowercase character!) * ``Capitalized_Words_With_Underscores`` -.. note:: When using abbreviations in CapWords, capitalize all the letters of the abbreviation. Thus HTTPServerError is better than HttpServerError. +.. note:: When using initialisms in CapWords, capitalize all the letters of the initialisms. Thus HTTPServerError is better than HttpServerError. When using initialisms is mixedCase, capitalize all the letters of the initialisms, except keep the first one lower case if it is the beginning of the name. Thus xmlHTTPRequest is better than XMLHTTPRequest. Names to Avoid diff --git a/docs/types.rst b/docs/types.rst index 55eaa69a..3611bc3e 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -470,7 +470,7 @@ Example that shows how to use internal function types:: Another example that uses external function types:: - pragma solidity ^0.4.11; + pragma solidity ^0.4.20; // should actually be 0.4.21 contract Oracle { struct Request { @@ -481,7 +481,7 @@ Another example that uses external function types:: event NewRequest(uint); function query(bytes data, function(bytes memory) external callback) public { requests.push(Request(data, callback)); - NewRequest(requests.length - 1); + emit NewRequest(requests.length - 1); } function reply(uint requestID, bytes response) public { // Here goes the check that the reply comes from a trusted source diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index ce58cf56..cc4d4446 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -107,9 +107,9 @@ Mathematical and Cryptographic Functions ---------------------------------------- ``addmod(uint x, uint y, uint k) returns (uint)``: - compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. + compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. ``mulmod(uint x, uint y, uint k) returns (uint)``: - compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. + compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. ``keccak256(...) returns (bytes32)``: compute the Ethereum-SHA-3 (Keccak-256) hash of the :ref:`(tightly packed) arguments <abi_packed_mode>` ``sha256(...) returns (bytes32)``: @@ -149,15 +149,15 @@ Address Related ``<address>.balance`` (``uint256``): balance of the :ref:`address` in Wei ``<address>.transfer(uint256 amount)``: - send given amount of Wei to :ref:`address`, throws on failure + send given amount of Wei to :ref:`address`, throws on failure, forwards 2300 gas stipend, not adjustable ``<address>.send(uint256 amount) returns (bool)``: - send given amount of Wei to :ref:`address`, returns ``false`` on failure + send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable ``<address>.call(...) returns (bool)``: - issue low-level ``CALL``, returns ``false`` on failure + issue low-level ``CALL``, returns ``false`` on failure, forwards all available gas, adjustable ``<address>.callcode(...) returns (bool)``: - issue low-level ``CALLCODE``, returns ``false`` on failure + issue low-level ``CALLCODE``, returns ``false`` on failure, forwards all available gas, adjustable ``<address>.delegatecall(...) returns (bool)``: - issue low-level ``DELEGATECALL``, returns ``false`` on failure + issue low-level ``DELEGATECALL``, returns ``false`` on failure, forwards all available gas, adjustable For more information, see the section on :ref:`address`. diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 42cc807a..66e3ac35 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -9,6 +9,9 @@ Using the compiler Using the Commandline Compiler ****************************** +.. note:: + This section doesn't apply to :ref:`solcjs <solcjs>`. + One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler. Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``. diff --git a/libdevcore/CMakeLists.txt b/libdevcore/CMakeLists.txt index a1c4c2d3..d107f701 100644 --- a/libdevcore/CMakeLists.txt +++ b/libdevcore/CMakeLists.txt @@ -2,7 +2,9 @@ file(GLOB sources "*.cpp") file(GLOB headers "*.h") add_library(devcore ${sources} ${headers}) -target_link_libraries(devcore PRIVATE ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(devcore PRIVATE ${JSONCPP_LIBRARY} ${Boost_FILESYSTEM_LIBRARIES} ${Boost_REGEX_LIBRARIES} ${Boost_SYSTEM_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) target_include_directories(devcore SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) target_include_directories(devcore PUBLIC "${CMAKE_SOURCE_DIR}") +target_include_directories(devcore PUBLIC "${JSONCPP_INCLUDE_DIR}") +add_dependencies(devcore jsoncpp) add_dependencies(devcore solidity_BuildInfo.h) diff --git a/libdevcore/JSON.cpp b/libdevcore/JSON.cpp new file mode 100644 index 00000000..079d4d51 --- /dev/null +++ b/libdevcore/JSON.cpp @@ -0,0 +1,109 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** @file JSON.cpp + * @author Alexander Arlt <alexander.arlt@arlt-labs.com> + * @date 2018 + */ + +#include "JSON.h" + +#include <sstream> +#include <map> +#include <memory> + +using namespace std; + +namespace dev +{ + +namespace +{ + +/// StreamWriterBuilder that can be constructed with specific settings +class StreamWriterBuilder: public Json::StreamWriterBuilder +{ +public: + explicit StreamWriterBuilder(map<string, string> const& _settings) + { + for (auto const& iter :_settings) + this->settings_[iter.first] = iter.second; + } +}; + +/// CharReaderBuilder with strict-mode settings +class StrictModeCharReaderBuilder: public Json::CharReaderBuilder +{ +public: + StrictModeCharReaderBuilder() + { + Json::CharReaderBuilder::strictMode(&this->settings_); + } +}; + +/// Serialise the JSON object (@a _input) with specific builder (@a _builder) +/// \param _input JSON input string +/// \param _builder StreamWriterBuilder that is used to create new Json::StreamWriter +/// \return serialized json object +string print(Json::Value const& _input, Json::StreamWriterBuilder const& _builder) +{ + stringstream stream; + unique_ptr<Json::StreamWriter> writer(_builder.newStreamWriter()); + writer->write(_input, &stream); + return stream.str(); +} + +/// Parse a JSON string (@a _input) with specified builder (@ _builder) and writes resulting JSON object to (@a _json) +/// \param _builder CharReaderBuilder that is used to create new Json::CharReaders +/// \param _input JSON input string +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool parse(Json::CharReaderBuilder& _builder, string const& _input, Json::Value& _json, string* _errs) +{ + unique_ptr<Json::CharReader> reader(_builder.newCharReader()); + return reader->parse(_input.c_str(), _input.c_str() + _input.length(), &_json, _errs); +} + +} // end anonymous namespace + +string jsonPrettyPrint(Json::Value const& _input) +{ + static map<string, string> settings{{"indentation", " "}}; + static StreamWriterBuilder writerBuilder(settings); + return print(_input, writerBuilder); +} + +string jsonCompactPrint(Json::Value const& _input) +{ + static map<string, string> settings{{"indentation", ""}}; + static StreamWriterBuilder writerBuilder(settings); + return print(_input, writerBuilder); +} + +bool jsonParseStrict(string const& _input, Json::Value& _json, string* _errs /* = nullptr */) +{ + static StrictModeCharReaderBuilder readerBuilder; + return parse(readerBuilder, _input, _json, _errs); +} + +bool jsonParse(string const& _input, Json::Value& _json, string *_errs /* = nullptr */) +{ + static Json::CharReaderBuilder readerBuilder; + return parse(readerBuilder, _input, _json, _errs); +} + +} // namespace dev diff --git a/libdevcore/JSON.h b/libdevcore/JSON.h index 8499d623..1ce822cd 100644 --- a/libdevcore/JSON.h +++ b/libdevcore/JSON.h @@ -24,21 +24,28 @@ #include <json/json.h> -namespace dev -{ +#include <string> + +namespace dev { /// Serialise the JSON object (@a _input) with indentation -inline std::string jsonPrettyPrint(Json::Value const& _input) -{ - return Json::StyledWriter().write(_input); -} +std::string jsonPrettyPrint(Json::Value const& _input); /// Serialise the JSON object (@a _input) without indentation -inline std::string jsonCompactPrint(Json::Value const& _input) -{ - Json::FastWriter writer; - writer.omitEndingLineFeed(); - return writer.write(_input); -} +std::string jsonCompactPrint(Json::Value const& _input); + +/// Parse a JSON string (@a _input) with enabled strict-mode and writes resulting JSON object to (@a _json) +/// \param _input JSON input string +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool jsonParseStrict(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); + +/// Parse a JSON string (@a _input) and writes resulting JSON object to (@a _json) +/// \param _input JSON input string +/// \param _json [out] resulting JSON object +/// \param _errs [out] Formatted error messages +/// \return \c true if the document was successfully parsed, \c false if an error occurred. +bool jsonParse(std::string const& _input, Json::Value& _json, std::string* _errs = nullptr); } diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp index b38981d2..a677a631 100644 --- a/libevmasm/Instruction.cpp +++ b/libevmasm/Instruction.cpp @@ -50,6 +50,9 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions = { "OR", Instruction::OR }, { "XOR", Instruction::XOR }, { "BYTE", Instruction::BYTE }, + { "SHL", Instruction::SHL }, + { "SHR", Instruction::SHR }, + { "SAR", Instruction::SAR }, { "ADDMOD", Instruction::ADDMOD }, { "MULMOD", Instruction::MULMOD }, { "SIGNEXTEND", Instruction::SIGNEXTEND }, @@ -190,6 +193,9 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo = { Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } }, + { Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } }, { Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } }, { Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } }, diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h index d9c53900..be788ddb 100644 --- a/libevmasm/Instruction.h +++ b/libevmasm/Instruction.h @@ -59,8 +59,11 @@ enum class Instruction: uint8_t AND, ///< bitwise AND operation OR, ///< bitwise OR operation XOR, ///< bitwise XOR operation - NOT, ///< bitwise NOT opertation + NOT, ///< bitwise NOT operation BYTE, ///< retrieve single byte from word + SHL, ///< bitwise SHL operation + SHR, ///< bitwise SHR operation + SAR, ///< bitwise SAR operation KECCAK256 = 0x20, ///< compute KECCAK-256 hash diff --git a/libjulia/backends/evm/EVMCodeTransform.cpp b/libjulia/backends/evm/EVMCodeTransform.cpp index 0c7365fb..2a97429b 100644 --- a/libjulia/backends/evm/EVMCodeTransform.cpp +++ b/libjulia/backends/evm/EVMCodeTransform.cpp @@ -522,7 +522,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) } } -int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) +int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const { solAssert(m_context->variableStackHeights.count(&_var), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; @@ -537,12 +537,12 @@ int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& return heightDiff; } -void CodeTransform::expectDeposit(int _deposit, int _oldHeight) +void CodeTransform::expectDeposit(int _deposit, int _oldHeight) const { solAssert(m_assembly.stackHeight() == _oldHeight + _deposit, "Invalid stack deposit."); } -void CodeTransform::checkStackHeight(void const* _astElement) +void CodeTransform::checkStackHeight(void const* _astElement) const { solAssert(m_info.stackHeightInfo.count(_astElement), "Stack height for AST element not found."); solAssert( diff --git a/libjulia/backends/evm/EVMCodeTransform.h b/libjulia/backends/evm/EVMCodeTransform.h index 0f2aaf95..f8eec0b7 100644 --- a/libjulia/backends/evm/EVMCodeTransform.h +++ b/libjulia/backends/evm/EVMCodeTransform.h @@ -133,11 +133,11 @@ private: /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns /// the (positive) stack height difference otherwise. - int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap); + int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; - void expectDeposit(int _deposit, int _oldHeight); + void expectDeposit(int _deposit, int _oldHeight) const; - void checkStackHeight(void const* _astElement); + void checkStackHeight(void const* _astElement) const; julia::AbstractAssembly& m_assembly; solidity::assembly::AsmAnalysisInfo& m_info; diff --git a/libjulia/optimiser/SimplificationRules.cpp b/libjulia/optimiser/SimplificationRules.cpp index e5dbb059..070d5484 100644 --- a/libjulia/optimiser/SimplificationRules.cpp +++ b/libjulia/optimiser/SimplificationRules.cpp @@ -41,7 +41,7 @@ SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expressio static SimplificationRules rules; - FunctionalInstruction const& instruction = boost::get<FunctionalInstruction const&>(_expr); + FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr); for (auto const& rule: rules.m_rules[byte(instruction.instruction)]) { rules.resetMatchGroups(); @@ -100,7 +100,7 @@ bool Pattern::matches(Expression const& _expr) const { if (_expr.type() != typeid(Literal)) return false; - Literal const& literal = boost::get<Literal const&>(_expr); + Literal const& literal = boost::get<Literal>(_expr); if (literal.kind != assembly::LiteralKind::Number) return false; if (m_data && *m_data != u256(literal.value)) @@ -111,7 +111,7 @@ bool Pattern::matches(Expression const& _expr) const { if (_expr.type() != typeid(FunctionalInstruction)) return false; - FunctionalInstruction const& instr = boost::get<FunctionalInstruction const&>(_expr); + FunctionalInstruction const& instr = boost::get<FunctionalInstruction>(_expr); if (m_instruction != instr.instruction) return false; assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, ""); @@ -168,7 +168,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const u256 Pattern::d() const { - Literal const& literal = boost::get<Literal const&>(matchGroupValue()); + Literal const& literal = boost::get<Literal>(matchGroupValue()); assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); return u256(literal.value); } diff --git a/libsolc/libsolc.cpp b/libsolc/libsolc.cpp index 3a6e1521..6c587e23 100644 --- a/libsolc/libsolc.cpp +++ b/libsolc/libsolc.cpp @@ -203,7 +203,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback { Json::Value contractInput = ret["contracts"][sourceName][contractName]; Json::Value contractOutput = Json::objectValue; - contractOutput["interface"] = dev::jsonCompactPrint(contractInput["abi"]); + contractOutput["interface"] = jsonCompactPrint(contractInput["abi"]); contractOutput["metadata"] = contractInput["metadata"]; contractOutput["functionHashes"] = contractInput["evm"]["methodIdentifiers"]; contractOutput["gasEstimates"] = translateGasEstimates(contractInput["evm"]["gasEstimates"]); @@ -219,7 +219,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback try { - return dev::jsonCompactPrint(output); + return jsonCompactPrint(output); } catch (...) { @@ -229,15 +229,15 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback _readCallback = nullptr) { - Json::Reader reader; + string errors; Json::Value input; - if (!reader.parse(_input, input, false)) + if (!jsonParseStrict(_input, input, &errors)) { - Json::Value errors(Json::arrayValue); - errors.append("Error parsing input JSON: " + reader.getFormattedErrorMessages()); + Json::Value jsonErrors(Json::arrayValue); + jsonErrors.append("Error parsing input JSON: " + errors); Json::Value output(Json::objectValue); - output["errors"] = errors; - return dev::jsonCompactPrint(output); + output["errors"] = jsonErrors; + return jsonCompactPrint(output); } else { diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 7508ad9e..c7ba78d6 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -79,6 +79,17 @@ Declaration const* DeclarationContainer::conflictingDeclaration( return nullptr; } +void DeclarationContainer::activateVariable(ASTString const& _name) +{ + solAssert( + m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1, + "Tried to activate a non-inactive variable or multiple inactive variables with the same name." + ); + solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), ""); + m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front()); + m_invisibleDeclarations.erase(_name); +} + bool DeclarationContainer::registerDeclaration( Declaration const& _declaration, ASTString const* _name, @@ -106,15 +117,17 @@ bool DeclarationContainer::registerDeclaration( return true; } -vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const +vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const { solAssert(!_name.empty(), "Attempt to resolve empty name."); - auto result = m_declarations.find(_name); - if (result != m_declarations.end()) - return result->second; - if (_recursive && m_enclosingContainer) - return m_enclosingContainer->resolveName(_name, true); - return vector<Declaration const*>({}); + vector<Declaration const*> result; + if (m_declarations.count(_name)) + result = m_declarations.at(_name); + if (_alsoInvisible && m_invisibleDeclarations.count(_name)) + result += m_invisibleDeclarations.at(_name); + if (result.empty() && _recursive && m_enclosingContainer) + result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible); + return result; } vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const @@ -129,6 +142,12 @@ vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) con if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) similar.push_back(declarationName); } + for (auto const& declaration: m_invisibleDeclarations) + { + string const& declarationName = declaration.first; + if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + similar.push_back(declarationName); + } if (m_enclosingContainer) similar += m_enclosingContainer->similarNames(_name); diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h index f9b1bda4..e4b3320a 100644 --- a/libsolidity/analysis/DeclarationContainer.h +++ b/libsolidity/analysis/DeclarationContainer.h @@ -51,13 +51,17 @@ public: /// @param _update if true, replaces a potential declaration that is already present /// @returns false if the name was already declared. bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false); - std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const; + std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const; ASTNode const* enclosingNode() const { return m_enclosingNode; } DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; } std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; } /// @returns whether declaration is valid, and if not also returns previous declaration. Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const; + /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// VariableDeclarationStatements. + void activateVariable(ASTString const& _name); + /// @returns existing declaration names similar to @a _name. /// Searches this and all parent containers. std::vector<ASTString> similarNames(ASTString const& _name) const; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index 5e4d414b..2f675135 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -50,12 +50,13 @@ NameAndTypeResolver::NameAndTypeResolver( m_scopes[nullptr]->registerDeclaration(*declaration); } -bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope) +bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope) { + bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050); // The helper registers all declarations in m_scopes as a side-effect of its construction. try { - DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope); + DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope); } catch (FatalError const&) { @@ -106,7 +107,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So else for (Declaration const* declaration: declarations) if (!DeclarationRegistrationHelper::registerDeclaration( - target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter + target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter )) error = true; } @@ -114,7 +115,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So for (auto const& nameAndDeclaration: scope->second->declarations()) for (auto const& declaration: nameAndDeclaration.second) if (!DeclarationRegistrationHelper::registerDeclaration( - target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter + target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter )) error = true; } @@ -151,6 +152,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration) return true; } +void NameAndTypeResolver::activateVariable(string const& _name) +{ + solAssert(m_currentScope, ""); + m_currentScope->activateVariable(_name); +} + vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const { auto iterator = m_scopes.find(_scope); @@ -159,15 +166,15 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na return iterator->second->resolveName(_name, false); } -vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const +vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const { - return m_currentScope->resolveName(_name, _recursive); + return m_currentScope->resolveName(_name, true, _includeInvisibles); } -Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const +Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path) const { solAssert(!_path.empty(), ""); - vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive); + vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), true); for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++) { if (!m_scopes.count(candidates.front())) @@ -229,7 +236,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() for (auto const& instruction: c_instructions) { string const instructionName{boost::algorithm::to_lower_copy(instruction.first)}; - auto declarations = nameFromCurrentScope(instructionName); + auto declarations = nameFromCurrentScope(instructionName, true); for (Declaration const* const declaration: declarations) { solAssert(!!declaration, ""); @@ -244,19 +251,24 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions() } } +void NameAndTypeResolver::setScope(ASTNode const* _node) +{ + m_currentScope = m_scopes[_node].get(); +} + bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode) { if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node)) { bool success = true; - m_currentScope = m_scopes[contract->scope()].get(); + setScope(contract->scope()); solAssert(!!m_currentScope, ""); for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts()) if (!resolveNamesAndTypes(*baseContract, true)) success = false; - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (success) { @@ -273,7 +285,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res // these can contain code, only resolve parameters for now for (ASTPointer<ASTNode> const& node: contract->subNodes()) { - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (!resolveNamesAndTypes(*node, false)) { success = false; @@ -287,12 +299,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!_resolveInsideCode) return success; - m_currentScope = m_scopes[contract].get(); + setScope(contract); // now resolve references inside the code for (ASTPointer<ASTNode> const& node: contract->subNodes()) { - m_currentScope = m_scopes[contract].get(); + setScope(contract); if (!resolveNamesAndTypes(*node, true)) success = false; } @@ -301,7 +313,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res else { if (m_scopes.count(&_node)) - m_currentScope = m_scopes[&_node].get(); + setScope(&_node); return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node); } } @@ -434,9 +446,11 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const DeclarationRegistrationHelper::DeclarationRegistrationHelper( map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, + bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope ): + m_useC99Scoping(_useC99Scoping), m_scopes(_scopes), m_currentScope(_currentScope), m_errorReporter(_errorReporter) @@ -451,18 +465,23 @@ bool DeclarationRegistrationHelper::registerDeclaration( string const* _name, SourceLocation const* _errorLocation, bool _warnOnShadow, + bool _inactive, ErrorReporter& _errorReporter ) { if (!_errorLocation) _errorLocation = &_declaration.location(); + string name = _name ? *_name : _declaration.name(); Declaration const* shadowedDeclaration = nullptr; - if (_warnOnShadow && !_declaration.name().empty() && _container.enclosingContainer()) - for (auto const* decl: _container.enclosingContainer()->resolveName(_declaration.name(), true)) + if (_warnOnShadow && !name.empty() && _container.enclosingContainer()) + for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true)) shadowedDeclaration = decl; - if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract())) + // We use "invisible" for both inactive variables in blocks and for members invisible in contracts. + // They cannot both be true at the same time. + solAssert(!(_inactive && !_declaration.isVisibleInContract()), ""); + if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive)) { SourceLocation firstDeclarationLocation; SourceLocation secondDeclarationLocation; @@ -604,6 +623,34 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) closeCurrentScope(); } +bool DeclarationRegistrationHelper::visit(Block& _block) +{ + _block.setScope(m_currentScope); + if (m_useC99Scoping) + enterNewSubScope(_block); + return true; +} + +void DeclarationRegistrationHelper::endVisit(Block&) +{ + if (m_useC99Scoping) + closeCurrentScope(); +} + +bool DeclarationRegistrationHelper::visit(ForStatement& _for) +{ + _for.setScope(m_currentScope); + if (m_useC99Scoping) + enterNewSubScope(_for); + return true; +} + +void DeclarationRegistrationHelper::endVisit(ForStatement&) +{ + if (m_useC99Scoping) + closeCurrentScope(); +} + void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement) { // Register the local variables with the function @@ -631,14 +678,14 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&) closeCurrentScope(); } -void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration) +void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope) { map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter; bool newlyAdded; shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get())); - tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container)); + tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container)); solAssert(newlyAdded, "Unable to add new scope."); - m_currentScope = &_declaration; + m_currentScope = &_subScope; } void DeclarationRegistrationHelper::closeCurrentScope() @@ -666,7 +713,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio if (fun->isConstructor()) warnAboutShadowing = false; - registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter); + // Register declaration as inactive if we are in block scope and C99 mode. + bool inactive = + m_useC99Scoping && + (dynamic_cast<Block const*>(m_currentScope) || dynamic_cast<ForStatement const*>(m_currentScope)); + + registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter); _declaration.setScope(m_currentScope); if (_opensScope) diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 9aea07ab..3d10fbd8 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -56,7 +56,7 @@ public: /// @returns false in case of error. /// @param _currentScope should be nullptr but can be used to inject new declarations into /// existing scopes, used by the snippets feature. - bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr); + bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr); /// Applies the effect of import directives. bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits); /// Resolves all names and types referenced from the given AST Node. @@ -69,20 +69,24 @@ public: /// that create their own scope. /// @returns false in case of error. bool updateDeclaration(Declaration const& _declaration); + /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for + /// VariableDeclarationStatements. + void activateVariable(std::string const& _name); /// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted, /// the global scope is used (i.e. the one containing only the pre-defined global variables). /// @returns a pointer to the declaration on success or nullptr on failure. + /// SHOULD only be used for testing. std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const; - /// Resolves a name in the "current" scope. Should only be called during the initial - /// resolving phase. - std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const; + /// Resolves a name in the "current" scope, but also searches parent scopes. + /// Should only be called during the initial resolving phase. + std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles = false) const; - /// Resolves a path starting from the "current" scope. Should only be called during the initial - /// resolving phase. + /// Resolves a path starting from the "current" scope, but also searches parent scopes. + /// Should only be called during the initial resolving phase. /// @note Returns a null pointer if any component in the path was not unique or not found. - Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const; + Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const; /// returns the vector of declarations without repetitions std::vector<Declaration const*> cleanedDeclarations( @@ -96,6 +100,9 @@ public: /// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions. std::string similarNameSuggestions(ASTString const& _name) const; + /// Sets the current scope. + void setScope(ASTNode const* _node); + private: /// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors. bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true); @@ -135,6 +142,7 @@ public: DeclarationRegistrationHelper( std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes, ASTNode& _astRoot, + bool _useC99Scoping, ErrorReporter& _errorReporter, ASTNode const* _currentScope = nullptr ); @@ -145,6 +153,7 @@ public: std::string const* _name, SourceLocation const* _errorLocation, bool _warnOnShadow, + bool _inactive, ErrorReporter& _errorReporter ); @@ -163,12 +172,16 @@ private: void endVisit(FunctionDefinition& _function) override; bool visit(ModifierDefinition& _modifier) override; void endVisit(ModifierDefinition& _modifier) override; + bool visit(Block& _block) override; + void endVisit(Block& _block) override; + bool visit(ForStatement& _forLoop) override; + void endVisit(ForStatement& _forLoop) override; void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override; bool visit(VariableDeclaration& _declaration) override; bool visit(EventDefinition& _event) override; void endVisit(EventDefinition& _event) override; - void enterNewSubScope(Declaration const& _declaration); + void enterNewSubScope(ASTNode& _subScope); void closeCurrentScope(); void registerDeclaration(Declaration& _declaration, bool _opensScope); @@ -177,6 +190,7 @@ private: /// @returns the canonical name of the current scope. std::string currentCanonicalName() const; + bool m_useC99Scoping = false; std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes; ASTNode const* m_currentScope = nullptr; VariableScope* m_currentFunction = nullptr; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 0bb5e3fe..985c44d0 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -43,6 +43,56 @@ bool ReferencesResolver::resolve(ASTNode const& _root) return !m_errorOccurred; } +bool ReferencesResolver::visit(Block const& _block) +{ + if (!m_resolveInsideCode) + return false; + m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(&_block); + return true; +} + +void ReferencesResolver::endVisit(Block const& _block) +{ + if (!m_resolveInsideCode) + return; + + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(_block.scope()); +} + +bool ReferencesResolver::visit(ForStatement const& _for) +{ + if (!m_resolveInsideCode) + return false; + m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050); + // C99-scoped variables + if (m_experimental050Mode) + m_resolver.setScope(&_for); + return true; +} + +void ReferencesResolver::endVisit(ForStatement const& _for) +{ + if (!m_resolveInsideCode) + return; + if (m_experimental050Mode) + m_resolver.setScope(_for.scope()); +} + +void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement) +{ + if (!m_resolveInsideCode) + return; + if (m_experimental050Mode) + for (auto const& var: _varDeclStatement.declarations()) + if (var) + m_resolver.activateVariable(var->name()); +} + bool ReferencesResolver::visit(Identifier const& _identifier) { auto declarations = m_resolver.nameFromCurrentScope(_identifier.name()); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index fef2e73f..4e8f54b5 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -57,7 +57,11 @@ public: bool resolve(ASTNode const& _root); private: - virtual bool visit(Block const&) override { return m_resolveInsideCode; } + virtual bool visit(Block const& _block) override; + virtual void endVisit(Block const& _block) override; + virtual bool visit(ForStatement const& _for) override; + virtual void endVisit(ForStatement const& _for) override; + virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; virtual bool visit(Identifier const& _identifier) override; virtual bool visit(ElementaryTypeName const& _typeName) override; virtual bool visit(FunctionDefinition const& _functionDefinition) override; @@ -90,6 +94,7 @@ private: std::vector<ParameterList const*> m_returnParameters; bool const m_resolveInsideCode; bool m_errorOccurred = false; + bool m_experimental050Mode = false; }; } diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 5a3745b0..ddac194b 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -93,8 +93,10 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma) m_errorReporter.syntaxError(_pragma.location(), "Duplicate experimental feature name."); else { - m_sourceUnit->annotation().experimentalFeatures.insert(ExperimentalFeatureNames.at(literal)); - m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); + auto feature = ExperimentalFeatureNames.at(literal); + m_sourceUnit->annotation().experimentalFeatures.insert(feature); + if (!ExperimentalFeatureOnlyAnalysis.count(feature)) + m_errorReporter.warning(_pragma.location(), "Experimental features are turned on. Do not use experimental features on live deployments."); } } } @@ -172,10 +174,18 @@ bool SyntaxChecker::visit(Break const& _breakStatement) bool SyntaxChecker::visit(Throw const& _throwStatement) { - m_errorReporter.warning( - _throwStatement.location(), - "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." - ); + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + + if (v050) + m_errorReporter.syntaxError( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); + else + m_errorReporter.warning( + _throwStatement.location(), + "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"." + ); return true; } @@ -202,13 +212,20 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(FunctionDefinition const& _function) { + bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050); + if (_function.noVisibilitySpecified()) - m_errorReporter.warning( - _function.location(), - "No visibility specified. Defaulting to \"" + - Declaration::visibilityToString(_function.visibility()) + - "\"." - ); + { + if (v050) + m_errorReporter.syntaxError(_function.location(), "No visibility specified."); + else + m_errorReporter.warning( + _function.location(), + "No visibility specified. Defaulting to \"" + + Declaration::visibilityToString(_function.visibility()) + + "\"." + ); + } return true; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index d67142e4..2914472a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -804,7 +804,12 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration, ""); if (auto var = dynamic_cast<VariableDeclaration const*>(declaration)) { - if (ref->second.isSlot || ref->second.isOffset) + if (var->isConstant()) + { + m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); + return size_t(-1); + } + else if (ref->second.isSlot || ref->second.isOffset) { if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage)) { @@ -817,11 +822,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) return size_t(-1); } } - else if (var->isConstant()) - { - m_errorReporter.typeError(_identifier.location, "Constant variables not supported by inline assembly."); - return size_t(-1); - } else if (!var->isLocalVariable()) { m_errorReporter.typeError(_identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes."); @@ -955,6 +955,16 @@ void TypeChecker::endVisit(Return const& _return) } } +void TypeChecker::endVisit(EmitStatement const& _emit) +{ + if ( + _emit.eventCall().annotation().kind != FunctionCallKind::FunctionCall || + dynamic_cast<FunctionType const&>(*type(_emit.eventCall().expression())).kind() != FunctionType::Kind::Event + ) + m_errorReporter.typeError(_emit.eventCall().expression().location(), "Expression has to be an event invocation."); + m_insideEmitStatement = false; +} + bool TypeChecker::visit(VariableDeclarationStatement const& _statement) { if (!_statement.initialValue()) @@ -972,7 +982,11 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) string errorText{"Uninitialized storage pointer."}; if (varDecl.referenceLocation() == VariableDeclaration::Location::Default) errorText += " Did you mean '<type> memory " + varDecl.name() + "'?"; - m_errorReporter.warning(varDecl.location(), errorText); + solAssert(m_scope, ""); + if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.declarationError(varDecl.location(), errorText); + else + m_errorReporter.warning(varDecl.location(), errorText); } } else if (dynamic_cast<MappingType const*>(type(varDecl).get())) @@ -1527,6 +1541,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) m_errorReporter.warning(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\""); } + if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event) + { + if (m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050)) + m_errorReporter.typeError(_functionCall.location(), "Event invocations have to be prefixed by \"emit\"."); + else + m_errorReporter.warning(_functionCall.location(), "Invoking events without \"emit\" prefix is deprecated."); + } TypePointers parameterTypes = functionType->parameterTypes(); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 344b019d..16796b63 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,6 +94,8 @@ private: virtual bool visit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; virtual void endVisit(Return const& _return) override; + virtual bool visit(EmitStatement const&) override { m_insideEmitStatement = true; return true; } + virtual void endVisit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variable) override; virtual void endVisit(ExpressionStatement const& _statement) override; virtual bool visit(Conditional const& _conditional) override; @@ -130,6 +132,9 @@ private: ContractDefinition const* m_scope = nullptr; + /// Flag indicating whether we are currently inside an EmitStatement. + bool m_insideEmitStatement = false; + ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 8da6964e..27220b1f 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -96,20 +96,6 @@ set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<Sour return sourceUnits; } -SourceUnit const& Declaration::sourceUnit() const -{ - solAssert(!!m_scope, ""); - ASTNode const* scope = m_scope; - while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope) - scope = dynamic_cast<Declaration const*>(scope)->m_scope; - return dynamic_cast<SourceUnit const&>(*scope); -} - -string Declaration::sourceUnitName() const -{ - return sourceUnit().annotation().path; -} - ImportAnnotation& ImportDirective::annotation() const { if (!m_annotation) @@ -408,12 +394,36 @@ UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation); } +SourceUnit const& Scopable::sourceUnit() const +{ + ASTNode const* s = scope(); + solAssert(s, ""); + // will not always be a declaratoion + 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); +} + +string Scopable::sourceUnitName() const +{ + return sourceUnit().annotation().path; +} + bool VariableDeclaration::isLValue() const { // External function parameters and constant declared variables are Read-Only return !isExternalCallableParameter() && !m_isConstant; } +bool VariableDeclaration::isLocalVariable() const +{ + auto s = scope(); + return + dynamic_cast<CallableDeclaration const*>(s) || + dynamic_cast<Block const*>(s) || + dynamic_cast<ForStatement const*>(s); +} + bool VariableDeclaration::isCallableParameter() const { auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); @@ -459,8 +469,7 @@ bool VariableDeclaration::isExternalCallableParameter() const bool VariableDeclaration::canHaveAutoType() const { - auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()); - return (!!callable && !isCallableParameter()); + return isLocalVariable() && !isCallableParameter(); } TypePointer VariableDeclaration::type() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index feffde64..863ad2fe 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -140,9 +140,32 @@ private: }; /** + * Abstract class that is added to each AST node that is stored inside a scope + * (including scopes). + */ +class Scopable +{ +public: + /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. + /// Available only after name and type resolution step. + ASTNode const* scope() const { return m_scope; } + void setScope(ASTNode const* _scope) { m_scope = _scope; } + + /// @returns the source unit this scopable is present in. + SourceUnit const& sourceUnit() const; + + /// @returns the source name this scopable is present in. + /// Can be combined with annotation().canonicalName (if present) to form a globally unique name. + std::string sourceUnitName() const; + +protected: + ASTNode const* m_scope = nullptr; +}; + +/** * Abstract AST class for a declaration (contract, function, struct, variable, import directive). */ -class Declaration: public ASTNode +class Declaration: public ASTNode, public Scopable { public: /// Visibility ordered from restricted to unrestricted. @@ -171,7 +194,7 @@ public: ASTPointer<ASTString> const& _name, Visibility _visibility = Visibility::Default ): - ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {} + ASTNode(_location), m_name(_name), m_visibility(_visibility) {} /// @returns the declared name. ASTString const& name() const { return *m_name; } @@ -181,17 +204,6 @@ public: virtual bool isVisibleInContract() const { return visibility() != Visibility::External; } bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; } - /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope. - /// Available only after name and type resolution step. - ASTNode const* scope() const { return m_scope; } - void setScope(ASTNode const* _scope) { m_scope = _scope; } - - /// @returns the source unit this declaration is present in. - SourceUnit const& sourceUnit() const; - - /// @returns the source name this declaration is present in. - /// Can be combined with annotation().canonicalName to form a globally unique name. - std::string sourceUnitName() const; std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); } virtual bool isLValue() const { return false; } @@ -213,7 +225,6 @@ protected: private: ASTPointer<ASTString> m_name; Visibility m_visibility; - ASTNode const* m_scope; }; /** @@ -289,6 +300,8 @@ private: /** * Abstract class that is added to each AST node that can store local variables. + * Local variables in functions are always added to functions, even though they are not + * in scope for the whole function. */ class VariableScope { @@ -662,7 +675,7 @@ public: virtual bool isLValue() const override; virtual bool isPartOfExternalInterface() const override { return isPublic(); } - bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); } + bool isLocalVariable() const; /// @returns true if this variable is a parameter or return parameter of a function. bool isCallableParameter() const; /// @returns true if this variable is a return parameter of a function. @@ -1004,7 +1017,7 @@ private: /** * Brace-enclosed block containing zero or more statements. */ -class Block: public Statement +class Block: public Statement, public Scopable { public: Block( @@ -1111,7 +1124,7 @@ private: /** * For loop statement */ -class ForStatement: public BreakableStatement +class ForStatement: public BreakableStatement, public Scopable { public: ForStatement( @@ -1197,6 +1210,27 @@ public: }; /** + * The emit statement is used to emit events: emit EventName(arg1, ..., argn) + */ +class EmitStatement: public Statement +{ +public: + explicit EmitStatement( + SourceLocation const& _location, + ASTPointer<ASTString> const& _docString, + ASTPointer<FunctionCall> const& _functionCall + ): + Statement(_location, _docString), m_eventCall(_functionCall) {} + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + FunctionCall const& eventCall() const { return *m_eventCall; } + +private: + ASTPointer<FunctionCall> m_eventCall; +}; + +/** * Definition of a variable as a statement inside a function. It requires a type name (which can * also be "var") but the actual assignment can be missing. * Examples: var a = 2; uint256 a; diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 46675e51..992fe4cd 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -69,6 +69,7 @@ class Continue; class Break; class Return; class Throw; +class EmitStatement; class VariableDeclarationStatement; class ExpressionStatement; class Expression; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 51249f20..4fef67c3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -324,6 +324,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) { std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.name()), + make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), // FIXME: remove with next breaking release make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View), make_pair("payable", _node.isPayable()), @@ -365,6 +366,7 @@ bool ASTJsonConverter::visit(ModifierDefinition const& _node) { setJsonNode(_node, "ModifierDefinition", { make_pair("name", _node.name()), + make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), make_pair("parameters", toJson(_node.parameterList())), make_pair("body", toJson(_node.body())) @@ -386,6 +388,7 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) m_inEvent = true; setJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()), + make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue), make_pair("parameters", toJson(_node.parameterList())), make_pair("anonymous", _node.isAnonymous()) }); @@ -537,7 +540,15 @@ bool ASTJsonConverter::visit(Return const& _node) bool ASTJsonConverter::visit(Throw const& _node) { - setJsonNode(_node, "Throw", {});; + setJsonNode(_node, "Throw", {}); + return false; +} + +bool ASTJsonConverter::visit(EmitStatement const& _node) +{ + setJsonNode(_node, "EmitStatement", { + make_pair("eventCall", toJson(_node.eventCall())) + }); return false; } diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 9a886220..88b93699 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -91,6 +91,7 @@ public: bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp index 23c3cbe1..4273f225 100644 --- a/libsolidity/ast/ASTPrinter.cpp +++ b/libsolidity/ast/ASTPrinter.cpp @@ -258,6 +258,13 @@ bool ASTPrinter::visit(Throw const& _node) return goDeeper(); } +bool ASTPrinter::visit(EmitStatement const& _node) +{ + writeLine("EmitStatement"); + printSourcePart(_node); + return goDeeper(); +} + bool ASTPrinter::visit(VariableDeclarationStatement const& _node) { writeLine("VariableDeclarationStatement"); @@ -517,6 +524,11 @@ void ASTPrinter::endVisit(Throw const&) m_indentation--; } +void ASTPrinter::endVisit(EmitStatement const&) +{ + m_indentation--; +} + void ASTPrinter::endVisit(VariableDeclarationStatement const&) { m_indentation--; diff --git a/libsolidity/ast/ASTPrinter.h b/libsolidity/ast/ASTPrinter.h index 01e4f7fc..de3bf8a2 100644 --- a/libsolidity/ast/ASTPrinter.h +++ b/libsolidity/ast/ASTPrinter.h @@ -76,6 +76,7 @@ public: bool visit(Break const& _node) override; bool visit(Return const& _node) override; bool visit(Throw const& _node) override; + bool visit(EmitStatement const& _node) override; bool visit(VariableDeclarationStatement const& _node) override; bool visit(ExpressionStatement const& _node) override; bool visit(Conditional const& _node) override; @@ -120,6 +121,7 @@ public: void endVisit(Break const&) override; void endVisit(Return const&) override; void endVisit(Throw const&) override; + void endVisit(EmitStatement const&) override; void endVisit(VariableDeclarationStatement const&) override; void endVisit(ExpressionStatement const&) override; void endVisit(Conditional const&) override; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index b726d592..b1389f0f 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -73,6 +73,7 @@ public: virtual bool visit(Break& _node) { return visitNode(_node); } virtual bool visit(Return& _node) { return visitNode(_node); } virtual bool visit(Throw& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement& _node) { return visitNode(_node); } virtual bool visit(Conditional& _node) { return visitNode(_node); } @@ -118,6 +119,7 @@ public: virtual void endVisit(Break& _node) { endVisitNode(_node); } virtual void endVisit(Return& _node) { endVisitNode(_node); } virtual void endVisit(Throw& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement& _node) { endVisitNode(_node); } virtual void endVisit(Conditional& _node) { endVisitNode(_node); } @@ -175,6 +177,7 @@ public: virtual bool visit(Break const& _node) { return visitNode(_node); } virtual bool visit(Return const& _node) { return visitNode(_node); } virtual bool visit(Throw const& _node) { return visitNode(_node); } + virtual bool visit(EmitStatement const& _node) { return visitNode(_node); } virtual bool visit(VariableDeclarationStatement const& _node) { return visitNode(_node); } virtual bool visit(ExpressionStatement const& _node) { return visitNode(_node); } virtual bool visit(Conditional const& _node) { return visitNode(_node); } @@ -220,6 +223,7 @@ public: virtual void endVisit(Break const& _node) { endVisitNode(_node); } virtual void endVisit(Return const& _node) { endVisitNode(_node); } virtual void endVisit(Throw const& _node) { endVisitNode(_node); } + virtual void endVisit(EmitStatement const& _node) { endVisitNode(_node); } virtual void endVisit(VariableDeclarationStatement const& _node) { endVisitNode(_node); } virtual void endVisit(ExpressionStatement const& _node) { endVisitNode(_node); } virtual void endVisit(Conditional const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index 904d9ff6..70ee997e 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -541,6 +541,20 @@ void Throw::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void EmitStatement::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + +void EmitStatement::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + m_eventCall->accept(_visitor); + _visitor.endVisit(*this); +} + void ExpressionStatement::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index e4b7e4fd..771ae643 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -131,28 +131,28 @@ namespace string parenthesizeIdentifier(string const& _internal) { - return "$_" + _internal + "_$"; + return "(" + _internal + ")"; } template <class Range> string identifierList(Range const&& _list) { - return parenthesizeIdentifier(boost::algorithm::join(_list, "_$_")); + return parenthesizeIdentifier(boost::algorithm::join(_list, ",")); } -string identifier(TypePointer const& _type) +string richIdentifier(TypePointer const& _type) { - return _type ? _type->identifier() : ""; + return _type ? _type->richIdentifier() : ""; } string identifierList(vector<TypePointer> const& _list) { - return identifierList(_list | boost::adaptors::transformed(identifier)); + return identifierList(_list | boost::adaptors::transformed(richIdentifier)); } string identifierList(TypePointer const& _type) { - return parenthesizeIdentifier(identifier(_type)); + return parenthesizeIdentifier(richIdentifier(_type)); } string identifierList(TypePointer const& _type1, TypePointer const& _type2) @@ -165,11 +165,22 @@ string identifierList(TypePointer const& _type1, TypePointer const& _type2) string parenthesizeUserIdentifier(string const& _internal) { - return parenthesizeIdentifier(boost::algorithm::replace_all_copy(_internal, "$", "$$$")); + return parenthesizeIdentifier(_internal); } } +string Type::escapeIdentifier(string const& _identifier) +{ + string ret = _identifier; + // FIXME: should be _$$$_ + boost::algorithm::replace_all(ret, "$", "$$$"); + boost::algorithm::replace_all(ret, ",", "_$_"); + boost::algorithm::replace_all(ret, "(", "$_"); + boost::algorithm::replace_all(ret, ")", "_$"); + return ret; +} + TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type) { solAssert(Token::isElementaryTypeName(_type.token()), @@ -334,7 +345,7 @@ IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): ); } -string IntegerType::identifier() const +string IntegerType::richIdentifier() const { if (isAddress()) return "t_address"; @@ -504,7 +515,7 @@ FixedPointType::FixedPointType(int _totalBits, int _fractionalDigits, FixedPoint ); } -string FixedPointType::identifier() const +string FixedPointType::richIdentifier() const { return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits); } @@ -936,7 +947,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } } -string RationalNumberType::identifier() const +string RationalNumberType::richIdentifier() const { return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str(); } @@ -1077,7 +1088,7 @@ bool StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) const return false; } -string StringLiteralType::identifier() const +string StringLiteralType::richIdentifier() const { // Since we have to return a valid identifier and the string itself may contain // anything, we hash it. @@ -1177,7 +1188,7 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c return MemberList::MemberMap{MemberList::Member{"length", make_shared<IntegerType>(8)}}; } -string FixedBytesType::identifier() const +string FixedBytesType::richIdentifier() const { return "t_bytes" + std::to_string(m_bytes); } @@ -1370,7 +1381,7 @@ bool ArrayType::isExplicitlyConvertibleTo(const Type& _convertTo) const return true; } -string ArrayType::identifier() const +string ArrayType::richIdentifier() const { string id; if (isString()) @@ -1604,7 +1615,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer) return copy; } -string ContractType::identifier() const +string ContractType::richIdentifier() const { return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id()); } @@ -1756,7 +1767,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const return this->m_struct == convertTo.m_struct; } -string StructType::identifier() const +string StructType::richIdentifier() const { return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix(); } @@ -1988,7 +1999,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); } -string EnumType::identifier() const +string EnumType::richIdentifier() const { return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id()); } @@ -2074,7 +2085,7 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const return false; } -string TupleType::identifier() const +string TupleType::richIdentifier() const { return "t_tuple" + identifierList(components()); } @@ -2153,32 +2164,19 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal m_stateMutability(_function.stateMutability()), m_declaration(&_function) { - TypePointers params; - vector<string> paramNames; - TypePointers retParams; - vector<string> retParamNames; - if (_isInternal && m_stateMutability == StateMutability::Payable) m_stateMutability = StateMutability::NonPayable; - params.reserve(_function.parameters().size()); - paramNames.reserve(_function.parameters().size()); for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) { - paramNames.push_back(var->name()); - params.push_back(var->annotation().type); + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); } - retParams.reserve(_function.returnParameters().size()); - retParamNames.reserve(_function.returnParameters().size()); for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters()) { - retParamNames.push_back(var->name()); - retParams.push_back(var->annotation().type); + m_returnParameterNames.push_back(var->name()); + m_returnParameterTypes.push_back(var->annotation().type); } - swap(params, m_parameterTypes); - swap(paramNames, m_parameterNames); - swap(retParams, m_returnParameterTypes); - swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(VariableDeclaration const& _varDecl): @@ -2186,16 +2184,14 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): m_stateMutability(StateMutability::View), m_declaration(&_varDecl) { - TypePointers paramTypes; - vector<string> paramNames; auto returnType = _varDecl.annotation().type; while (true) { if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get())) { - paramTypes.push_back(mappingType->keyType()); - paramNames.push_back(""); + m_parameterTypes.push_back(mappingType->keyType()); + m_parameterNames.push_back(""); returnType = mappingType->valueType(); } else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get())) @@ -2204,15 +2200,13 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): // Return byte arrays as as whole. break; returnType = arrayType->baseType(); - paramNames.push_back(""); - paramTypes.push_back(make_shared<IntegerType>(256)); + m_parameterNames.push_back(""); + m_parameterTypes.push_back(make_shared<IntegerType>(256)); } else break; } - TypePointers retParams; - vector<string> retParamNames; if (auto structType = dynamic_cast<StructType const*>(returnType.get())) { for (auto const& member: structType->members(nullptr)) @@ -2223,24 +2217,22 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl): if (auto arrayType = dynamic_cast<ArrayType const*>(member.type.get())) if (!arrayType->isByteArray()) continue; - retParams.push_back(member.type); - retParamNames.push_back(member.name); + m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference( + DataLocation::Memory, + member.type + )); + m_returnParameterNames.push_back(member.name); } } } else { - retParams.push_back(ReferenceType::copyForLocationIfReference( + m_returnParameterTypes.push_back(ReferenceType::copyForLocationIfReference( DataLocation::Memory, returnType )); - retParamNames.push_back(""); + m_returnParameterNames.push_back(""); } - - swap(paramTypes, m_parameterTypes); - swap(paramNames, m_parameterNames); - swap(retParams, m_returnParameterTypes); - swap(retParamNames, m_returnParameterNames); } FunctionType::FunctionType(EventDefinition const& _event): @@ -2248,17 +2240,11 @@ FunctionType::FunctionType(EventDefinition const& _event): m_stateMutability(StateMutability::NonPayable), m_declaration(&_event) { - TypePointers params; - vector<string> paramNames; - params.reserve(_event.parameters().size()); - paramNames.reserve(_event.parameters().size()); for (ASTPointer<VariableDeclaration> const& var: _event.parameters()) { - paramNames.push_back(var->name()); - params.push_back(var->annotation().type); + m_parameterNames.push_back(var->name()); + m_parameterTypes.push_back(var->annotation().type); } - swap(params, m_parameterTypes); - swap(paramNames, m_parameterNames); } FunctionType::FunctionType(FunctionTypeName const& _typeName): @@ -2334,7 +2320,7 @@ TypePointers FunctionType::parameterTypes() const return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); } -string FunctionType::identifier() const +string FunctionType::richIdentifier() const { string id = "t_function_"; switch (m_kind) @@ -2827,7 +2813,7 @@ ASTPointer<ASTString> FunctionType::documentation() const return ASTPointer<ASTString>(); } -string MappingType::identifier() const +string MappingType::richIdentifier() const { return "t_mapping" + identifierList(m_keyType, m_valueType); } @@ -2850,7 +2836,7 @@ string MappingType::canonicalName() const return "mapping(" + keyType()->canonicalName() + " => " + valueType()->canonicalName() + ")"; } -string TypeType::identifier() const +string TypeType::richIdentifier() const { return "t_type" + identifierList(actualType()); } @@ -2935,7 +2921,7 @@ u256 ModifierType::storageSize() const solAssert(false, "Storage size of non-storable type type requested."); } -string ModifierType::identifier() const +string ModifierType::richIdentifier() const { return "t_modifier" + identifierList(m_parameterTypes); } @@ -2964,7 +2950,7 @@ string ModifierType::toString(bool _short) const return name + ")"; } -string ModuleType::identifier() const +string ModuleType::richIdentifier() const { return "t_module_" + std::to_string(m_sourceUnit.id()); } @@ -2990,7 +2976,7 @@ string ModuleType::toString(bool) const return string("module \"") + m_sourceUnit.annotation().path + string("\""); } -string MagicType::identifier() const +string MagicType::richIdentifier() const { switch (m_kind) { diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 2e7d05ba..7985521e 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -163,10 +163,20 @@ public: /// @returns a valid solidity identifier such that two types should compare equal if and /// only if they have the same identifier. /// The identifier should start with "t_". + /// Can contain characters which are invalid in identifiers. + virtual std::string richIdentifier() const = 0; + /// @returns a valid solidity identifier such that two types should compare equal if and + /// only if they have the same identifier. + /// The identifier should start with "t_". + /// Will not contain any character which would be invalid as an identifier. + std::string identifier() const { return escapeIdentifier(richIdentifier()); } + /// More complex identifier strings use "parentheses", where $_ is interpreted as as /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that /// appears as part of a user-supplied identifier is escaped as _$$$_. - virtual std::string identifier() const = 0; + /// @returns an escaped identifier (will not contain any parenthesis or commas) + static std::string escapeIdentifier(std::string const& _identifier); + virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const { @@ -306,7 +316,7 @@ public: explicit IntegerType(int _bits, Modifier _modifier = Modifier::Unsigned); - virtual std::string identifier() const override; + virtual std::string richIdentifier() 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; @@ -353,7 +363,7 @@ public: explicit FixedPointType(int _totalBits, int _fractionalDigits, Modifier _modifier = Modifier::Unsigned); - virtual std::string identifier() const override; + virtual std::string richIdentifier() 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; @@ -410,7 +420,7 @@ public: virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -459,7 +469,7 @@ public: return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } @@ -493,7 +503,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -521,7 +531,7 @@ class BoolType: public Type public: BoolType() {} virtual Category category() const override { return Category::Bool; } - virtual std::string identifier() const override { return "t_bool"; } + virtual std::string richIdentifier() const override { return "t_bool"; } virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; @@ -621,7 +631,7 @@ public: virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(const Type& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallySized() const override { return m_hasDynamicLength; } @@ -678,7 +688,7 @@ public: /// Contracts can be converted to themselves and to integers. virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded ) const override { @@ -736,7 +746,7 @@ public: explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage): ReferenceType(_location), m_struct(_struct) {} virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override; virtual bool isDynamicallyEncoded() const override; @@ -791,7 +801,7 @@ public: virtual Category category() const override { return Category::Enum; } explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {} virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual unsigned calldataEncodedSize(bool _padded) const override { @@ -832,7 +842,7 @@ public: virtual Category category() const override { return Category::Tuple; } explicit TupleType(std::vector<TypePointer> const& _types = std::vector<TypePointer>()): m_components(_types) {} virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } virtual std::string toString(bool) const override; @@ -966,7 +976,7 @@ public: /// @returns the "self" parameter type for a bound function TypePointer const& selfType() const; - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; @@ -1070,7 +1080,7 @@ public: MappingType(TypePointer const& _keyType, TypePointer const& _valueType): m_keyType(_keyType), m_valueType(_valueType) {} - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName() const override; @@ -1107,7 +1117,7 @@ public: TypePointer const& actualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual u256 storageSize() const override; @@ -1135,7 +1145,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override { return 0; } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -1156,7 +1166,7 @@ public: explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {} virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1186,7 +1196,7 @@ public: return TypePointer(); } - virtual std::string identifier() const override; + virtual std::string richIdentifier() const override; virtual bool operator==(Type const& _other) const override; virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } @@ -1210,7 +1220,7 @@ class InaccessibleDynamicType: public Type public: virtual Category category() const override { return Category::InaccessibleDynamic; } - virtual std::string identifier() const override { return "t_inaccessible"; } + virtual std::string richIdentifier() const override { return "t_inaccessible"; } virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; } virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 7a88475a..0198a107 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -319,7 +319,7 @@ void CompilerContext::appendInlineAssembly( ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--"); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner, false); #ifdef SOL_OUTPUT_ASM cout << assembly::AsmPrinter()(*parserResult) << endl; #endif diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index f463db94..ebb718a5 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -903,6 +903,15 @@ bool ContractCompiler::visit(Throw const& _throw) return false; } +bool ContractCompiler::visit(EmitStatement const& _emit) +{ + CompilerContext::LocationSetter locationSetter(m_context, _emit); + StackHeightChecker checker(m_context); + compileExpression(_emit.eventCall()); + checker.check(); + return false; +} + bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement) { StackHeightChecker checker(m_context); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 1fd80d05..d698dc71 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -109,6 +109,7 @@ private: virtual bool visit(Break const& _breakStatement) override; virtual bool visit(Return const& _return) override; virtual bool visit(Throw const& _throw) override; + virtual bool visit(EmitStatement const& _emit) override; virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override; virtual bool visit(ExpressionStatement const& _expressionStatement) override; virtual bool visit(PlaceholderStatement const&) override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 8e1cf019..61920592 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -765,7 +765,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) case FunctionType::Kind::AddMod: case FunctionType::Kind::MulMod: { - for (unsigned i = 0; i < 3; i ++) + arguments[2]->accept(*this); + utils().convertType(*arguments[2]->annotation().type, IntegerType(256)); + m_context << Instruction::DUP1 << Instruction::ISZERO; + m_context.appendConditionalInvalid(); + for (unsigned i = 1; i < 3; i ++) { arguments[2 - i]->accept(*this); utils().convertType(*arguments[2 - i]->annotation().type, IntegerType(256)); diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp index 2d6e58de..1030523a 100644 --- a/libsolidity/inlineasm/AsmAnalysis.cpp +++ b/libsolidity/inlineasm/AsmAnalysis.cpp @@ -82,6 +82,19 @@ bool AsmAnalyzer::operator()(assembly::Literal const& _literal) ); return false; } + else if (_literal.kind == assembly::LiteralKind::Number && bigint(_literal.value) > u256(-1)) + { + m_errorReporter.typeError( + _literal.location, + "Number literal too large (> 256 bits)" + ); + return false; + } + else if (_literal.kind == assembly::LiteralKind::Boolean) + { + solAssert(m_flavour == AsmFlavour::IULIA, ""); + solAssert(_literal.value == "true" || _literal.value == "false", ""); + } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } @@ -535,6 +548,20 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio "the Metropolis hard fork. Before that it acts as an invalid instruction." ); + static set<solidity::Instruction> experimentalInstructions{ + solidity::Instruction::SHL, + solidity::Instruction::SHR, + solidity::Instruction::SAR + }; + if (experimentalInstructions.count(_instr)) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available after " + + "the Constantinople hard fork. Before that it acts as an invalid instruction." + ); + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) m_errorReporter.warning( _location, diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp index 306b07e6..7f618e07 100644 --- a/libsolidity/inlineasm/AsmParser.cpp +++ b/libsolidity/inlineasm/AsmParser.cpp @@ -34,13 +34,16 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner) +shared_ptr<assembly::Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner) { m_recursionDepth = 0; try { m_scanner = _scanner; - return make_shared<Block>(parseBlock()); + auto block = make_shared<Block>(parseBlock()); + if (!_reuseScanner) + expectToken(Token::EOS); + return block; } catch (FatalError const&) { diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h index 015aeef3..41117228 100644 --- a/libsolidity/inlineasm/AsmParser.h +++ b/libsolidity/inlineasm/AsmParser.h @@ -41,8 +41,9 @@ public: ParserBase(_errorReporter), m_flavour(_flavour) {} /// Parses an inline assembly block starting with `{` and ending with `}`. + /// @param _reuseScanner if true, do check for end of input after the `}`. /// @returns an empty shared pointer on error. - std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner); + std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner); protected: using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>; diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp index 1b4bd270..c9e534c7 100644 --- a/libsolidity/interface/AssemblyStack.cpp +++ b/libsolidity/interface/AssemblyStack.cpp @@ -69,7 +69,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string m_errors.clear(); m_analysisSuccessful = false; m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName); - m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner); + m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner, false); if (!m_errorReporter.errors().empty()) return false; solAssert(m_parserResult, ""); diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp index aeafaf2d..9d02c498 100644 --- a/libsolidity/interface/SourceReferenceFormatter.cpp +++ b/libsolidity/interface/SourceReferenceFormatter.cpp @@ -31,15 +31,11 @@ namespace dev namespace solidity { -void SourceReferenceFormatter::printSourceLocation( - ostream& _stream, - SourceLocation const* _location, - function<Scanner const&(string const&)> const& _scannerFromSourceName -) +void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location) { if (!_location || !_location->sourceName) return; // Nothing we can print here - auto const& scanner = _scannerFromSourceName(*_location->sourceName); + auto const& scanner = m_scannerFromSourceName(*_location->sourceName); int startLine; int startColumn; tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start); @@ -64,21 +60,22 @@ void SourceReferenceFormatter::printSourceLocation( endColumn = startColumn + locationLength; } - _stream << line << endl; + m_stream << line << endl; + for_each( line.cbegin(), line.cbegin() + startColumn, - [&_stream](char const& ch) { _stream << (ch == '\t' ? '\t' : ' '); } + [this](char const& ch) { m_stream << (ch == '\t' ? '\t' : ' '); } ); - _stream << "^"; + m_stream << "^"; if (endColumn > startColumn + 2) - _stream << string(endColumn - startColumn - 2, '-'); + m_stream << string(endColumn - startColumn - 2, '-'); if (endColumn > startColumn + 1) - _stream << "^"; - _stream << endl; + m_stream << "^"; + m_stream << endl; } else - _stream << + m_stream << scanner.lineAtPosition(_location->start) << endl << string(startColumn, ' ') << @@ -86,50 +83,44 @@ void SourceReferenceFormatter::printSourceLocation( "Spanning multiple lines.\n"; } -void SourceReferenceFormatter::printSourceName( - ostream& _stream, - SourceLocation const* _location, - function<Scanner const&(string const&)> const& _scannerFromSourceName -) +void SourceReferenceFormatter::printSourceName(SourceLocation const* _location) { if (!_location || !_location->sourceName) return; // Nothing we can print here - auto const& scanner = _scannerFromSourceName(*_location->sourceName); + auto const& scanner = m_scannerFromSourceName(*_location->sourceName); int startLine; int startColumn; tie(startLine, startColumn) = scanner.translatePositionToLineColumn(_location->start); - _stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; + m_stream << *_location->sourceName << ":" << (startLine + 1) << ":" << (startColumn + 1) << ": "; } void SourceReferenceFormatter::printExceptionInformation( - ostream& _stream, Exception const& _exception, - string const& _name, - function<Scanner const&(string const&)> const& _scannerFromSourceName + string const& _name ) { SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception); auto secondarylocation = boost::get_error_info<errinfo_secondarySourceLocation>(_exception); - printSourceName(_stream, location, _scannerFromSourceName); + printSourceName(location); - _stream << _name; + m_stream << _name; if (string const* description = boost::get_error_info<errinfo_comment>(_exception)) - _stream << ": " << *description << endl; + m_stream << ": " << *description << endl; else - _stream << endl; + m_stream << endl; - printSourceLocation(_stream, location, _scannerFromSourceName); + printSourceLocation(location); if (secondarylocation && !secondarylocation->infos.empty()) { for (auto info: secondarylocation->infos) { - printSourceName(_stream, &info.second, _scannerFromSourceName); - _stream << info.first << endl; - printSourceLocation(_stream, &info.second, _scannerFromSourceName); + printSourceName(&info.second); + m_stream << info.first << endl; + printSourceLocation(&info.second); } - _stream << endl; + m_stream << endl; } } diff --git a/libsolidity/interface/SourceReferenceFormatter.h b/libsolidity/interface/SourceReferenceFormatter.h index e8676d60..a32babdc 100644 --- a/libsolidity/interface/SourceReferenceFormatter.h +++ b/libsolidity/interface/SourceReferenceFormatter.h @@ -38,22 +38,23 @@ namespace solidity class Scanner; // forward class CompilerStack; // forward -struct SourceReferenceFormatter +class SourceReferenceFormatter { public: using ScannerFromSourceNameFun = std::function<Scanner const&(std::string const&)>; - /// Prints source location if it is given. - static void printSourceLocation( - std::ostream& _stream, - SourceLocation const* _location, - ScannerFromSourceNameFun const& _scannerFromSourceName - ); - static void printExceptionInformation( + + explicit SourceReferenceFormatter( std::ostream& _stream, - Exception const& _exception, - std::string const& _name, - ScannerFromSourceNameFun const& _scannerFromSourceName - ); + ScannerFromSourceNameFun _scannerFromSourceName + ): + m_stream(_stream), + m_scannerFromSourceName(std::move(_scannerFromSourceName)) + {} + + /// Prints source location if it is given. + void printSourceLocation(SourceLocation const* _location); + void printExceptionInformation(Exception const& _exception, std::string const& _name); + static std::string formatExceptionInformation( Exception const& _exception, std::string const& _name, @@ -61,16 +62,17 @@ public: ) { std::ostringstream errorOutput; - printExceptionInformation(errorOutput, _exception, _name, _scannerFromSourceName); + + SourceReferenceFormatter formatter(errorOutput, _scannerFromSourceName); + formatter.printExceptionInformation(_exception, _name); return errorOutput.str(); } private: /// Prints source name if location is given. - static void printSourceName( - std::ostream& _stream, - SourceLocation const* _location, - ScannerFromSourceNameFun const& _scannerFromSourceName - ); + void printSourceName(SourceLocation const* _location); + + std::ostream& m_stream; + ScannerFromSourceNameFun m_scannerFromSourceName; }; } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 04f5bd25..8c64c164 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -236,7 +236,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) return formatFatalError("JSONError", "Only \"Solidity\" is supported as a language."); Json::Value const& sources = _input["sources"]; - if (!sources) + + if (!sources.isObject() && !sources.isNull()) + return formatFatalError("JSONError", "\"sources\" is not a JSON object."); + + if (sources.empty()) return formatFatalError("JSONError", "No input sources specified."); Json::Value errors = Json::arrayValue; @@ -323,13 +327,29 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input) m_compilerStack.setOptimiserSettings(optimize, optimizeRuns); map<string, h160> libraries; - Json::Value jsonLibraries = settings.get("libraries", Json::Value()); + Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue)); + if (!jsonLibraries.isObject()) + return formatFatalError("JSONError", "\"libraries\" is not a JSON object."); for (auto const& sourceName: jsonLibraries.getMemberNames()) { auto const& jsonSourceName = jsonLibraries[sourceName]; + if (!jsonSourceName.isObject()) + return formatFatalError("JSONError", "library entry is not a JSON object."); for (auto const& library: jsonSourceName.getMemberNames()) - // @TODO use libraries only for the given source - libraries[library] = h160(jsonSourceName[library].asString()); + { + try + { + // @TODO use libraries only for the given source + libraries[library] = h160(jsonSourceName[library].asString()); + } + catch (dev::BadHexCharacter) + { + return formatFatalError( + "JSONError", + "Invalid library address (\"" + jsonSourceName[library].asString() + "\") supplied." + ); + } + } } m_compilerStack.setLibraries(libraries); @@ -550,12 +570,11 @@ Json::Value StandardCompiler::compile(Json::Value const& _input) string StandardCompiler::compile(string const& _input) { Json::Value input; - Json::Reader reader; - + string errors; try { - if (!reader.parse(_input, input, false)) - return jsonCompactPrint(formatFatalError("JSONError", reader.getFormattedErrorMessages())); + if (!jsonParseStrict(_input, input, &errors)) + return jsonCompactPrint(formatFatalError("JSONError", errors)); } catch(...) { diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 05b877b5..8c97f55f 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -897,7 +897,9 @@ ASTPointer<Statement> Parser::parseStatement() case Token::Assembly: return parseInlineAssembly(docString); case Token::Identifier: - if (m_insideModifier && m_scanner->currentLiteral() == "_") + if (m_scanner->currentLiteral() == "emit") + statement = parseEmitStatement(docString); + else if (m_insideModifier && m_scanner->currentLiteral() == "_") { statement = ASTNodeFactory(*this).createNode<PlaceholderStatement>(docString); m_scanner->next(); @@ -926,7 +928,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con } assembly::Parser asmParser(m_errorReporter); - shared_ptr<assembly::Block> block = asmParser.parse(m_scanner); + shared_ptr<assembly::Block> block = asmParser.parse(m_scanner, true); nodeFactory.markEndPosition(); return nodeFactory.createNode<InlineAssembly>(_docString, block); } @@ -1015,6 +1017,38 @@ ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& ); } +ASTPointer<EmitStatement> Parser::parseEmitStatement(ASTPointer<ASTString> const& _docString) +{ + ASTNodeFactory nodeFactory(*this); + m_scanner->next(); + ASTNodeFactory eventCallNodeFactory(*this); + + if (m_scanner->currentToken() != Token::Identifier) + fatalParserError("Expected event name or path."); + + vector<ASTPointer<PrimaryExpression>> path; + while (true) + { + path.push_back(parseIdentifier()); + if (m_scanner->currentToken() != Token::Period) + break; + m_scanner->next(); + }; + + auto eventName = expressionFromIndexAccessStructure(path, {}); + expectToken(Token::LParen); + + vector<ASTPointer<Expression>> arguments; + vector<ASTPointer<ASTString>> names; + std::tie(arguments, names) = parseFunctionCallArguments(); + eventCallNodeFactory.markEndPosition(); + nodeFactory.markEndPosition(); + expectToken(Token::RParen); + auto eventCall = eventCallNodeFactory.createNode<FunctionCall>(eventName, arguments, names); + auto statement = nodeFactory.createNode<EmitStatement>(_docString, eventCall); + return statement; +} + ASTPointer<Statement> Parser::parseSimpleStatement(ASTPointer<ASTString> const& _docString) { RecursionGuard recursionGuard(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index cfdfea7e..3f780af9 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -104,6 +104,7 @@ private: ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString); ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString); ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString); + ASTPointer<EmitStatement> parseEmitStatement(ASTPointer<ASTString> const& docString); /// A "simple statement" can be a variable declaration statement or an expression statement. ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString); ASTPointer<VariableDeclarationStatement> parseVariableDeclarationStatement( diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index d62cffb7..e884ed65 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -168,11 +168,12 @@ case $(uname -s) in # Debian #------------------------------------------------------------------------------ - Debian) + Debian*) #Debian + . /etc/os-release install_z3="" - case $(lsb_release -cs) in - wheezy) + case $VERSION_ID in + 7) #wheezy echo "Installing solidity dependencies on Debian Wheezy (7.x)." echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet." @@ -182,16 +183,16 @@ case $(uname -s) in echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." exit 1 ;; - jessie) + 8) #jessie echo "Installing solidity dependencies on Debian Jesse (8.x)." ;; - stretch) + 9) #stretch echo "Installing solidity dependencies on Debian Stretch (9.x)." install_z3="libz3-dev" ;; - buster) + 10) #buster echo "Installing solidity dependencies on Debian Buster (10.x)." install_z3="libz3-dev" diff --git a/scripts/tests.sh b/scripts/tests.sh index 2b47c254..3c80adc5 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -30,49 +30,81 @@ set -e REPO_ROOT="$(dirname "$0")"/.. -echo "Running commandline tests..." -"$REPO_ROOT/test/cmdlineTests.sh" - -# This conditional is only needed because we don't have a working Homebrew -# install for `eth` at the time of writing, so we unzip the ZIP file locally -# instead. This will go away soon. -if [[ "$OSTYPE" == "darwin"* ]]; then - ETH_PATH="$REPO_ROOT/eth" -elif [ -z $CI ]; then - ETH_PATH="eth" +if [ "$1" = --junit_report ] +then + if [ -z "$2" ] + then + echo "Usage: $0 [--junit_report <report_directory>]" + exit 1 + fi + testargs_no_opt="--logger=JUNIT,test_suite,$2/no_opt.xml" + testargs_opt="--logger=JUNIT,test_suite,$2/opt.xml" else - mkdir -p /tmp/test - # Update hash below if binary is changed. - wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/eth_byzantium2 - test "$(shasum /tmp/test/eth)" = "4dc3f208475f622be7c8e53bee720e14cd254c6f /tmp/test/eth" - sync - chmod +x /tmp/test/eth - sync # Otherwise we might get a "text file busy" error - ETH_PATH="/tmp/test/eth" + testargs_no_opt='' + testargs_opt='' +fi + +echo "Running commandline tests..." +"$REPO_ROOT/test/cmdlineTests.sh" & +CMDLINE_PID=$! +# Only run in parallel if this is run on CI infrastructure +if [ -z "$CI" ] +then + wait $CMDLINE_PID fi -# This trailing ampersand directs the shell to run the command in the background, -# that is, it is forked and run in a separate sub-shell, as a job, -# asynchronously. The shell will immediately return the return status of 0 for -# true and continue as normal, either processing further commands in a script -# or returning the cursor focus back to the user in a Linux terminal. -$ETH_PATH --test -d /tmp/test & -ETH_PID=$! +function download_eth() +{ + if [[ "$OSTYPE" == "darwin"* ]]; then + ETH_PATH="$REPO_ROOT/eth" + elif [ -z $CI ]; then + ETH_PATH="eth" + else + mkdir -p /tmp/test + ETH_BINARY=eth_byzantium_artful + ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e" + if grep -i trusty /etc/lsb-release >/dev/null 2>&1 + then + ETH_BINARY=eth_byzantium2 + ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f" + fi + wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY + test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth" + sync + chmod +x /tmp/test/eth + sync # Otherwise we might get a "text file busy" error + ETH_PATH="/tmp/test/eth" + fi + +} + +# $1: data directory +# echos the PID +function run_eth() +{ + $ETH_PATH --test -d "$1" >/dev/null 2>&1 & + echo $! + # Wait until the IPC endpoint is available. + while [ ! -S "$1"/geth.ipc ] ; do sleep 1; done + sleep 2 +} + +download_eth +ETH_PID=$(run_eth /tmp/test) + +progress="--show-progress" +if [ "$CI" ] +then + progress="" +fi -# Wait until the IPC endpoint is available. That won't be available instantly. -# The node needs to get a little way into its startup sequence before the IPC -# is available and is ready for the unit-tests to start talking to it. -while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done -echo "--> IPC available." -sleep 2 -# And then run the Solidity unit-tests (once without optimization, once with), -# pointing to that IPC endpoint. echo "--> Running tests without optimizer..." - "$REPO_ROOT"/build/test/soltest --show-progress -- --ipcpath /tmp/test/geth.ipc && \ - echo "--> Running tests WITH optimizer..." && \ - "$REPO_ROOT"/build/test/soltest --show-progress -- --optimize --ipcpath /tmp/test/geth.ipc -ERROR_CODE=$? +"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc +echo "--> Running tests WITH optimizer..." +"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc + +wait $CMDLINE_PID + pkill "$ETH_PID" || true sleep 4 -pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true -exit $ERROR_CODE +pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
\ No newline at end of file diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index adcfee9c..62b24975 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -627,6 +627,7 @@ Allowed options)", try { po::command_line_parser cmdLineParser(_argc, _argv); + cmdLineParser.style(po::command_line_style::default_style & (~po::command_line_style::allow_guessing)); cmdLineParser.options(allOptions).positional(filesPositions); po::store(cmdLineParser.run(), m_args); } @@ -777,7 +778,10 @@ bool CommandLineInterface::processInput() } m_compiler.reset(new CompilerStack(fileReader)); + auto scannerFromSourceName = [&](string const& _sourceName) -> solidity::Scanner const& { return m_compiler->scanner(_sourceName); }; + SourceReferenceFormatter formatter(cerr, scannerFromSourceName); + try { if (m_args.count(g_argMetadataLiteral) > 0) @@ -796,11 +800,9 @@ bool CommandLineInterface::processInput() bool successful = m_compiler->compile(); for (auto const& error: m_compiler->errors()) - SourceReferenceFormatter::printExceptionInformation( - cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - scannerFromSourceName + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); if (!successful) @@ -808,7 +810,7 @@ bool CommandLineInterface::processInput() } catch (CompilerError const& _exception) { - SourceReferenceFormatter::printExceptionInformation(cerr, _exception, "Compiler error", scannerFromSourceName); + formatter.printExceptionInformation(_exception, "Compiler error"); return false; } catch (InternalCompilerError const& _exception) @@ -828,7 +830,7 @@ bool CommandLineInterface::processInput() if (_error.type() == Error::Type::DocstringParsingError) cerr << "Documentation parsing error: " << *boost::get_error_info<errinfo_comment>(_error) << endl; else - SourceReferenceFormatter::printExceptionInformation(cerr, _error, _error.typeName(), scannerFromSourceName); + formatter.printExceptionInformation(_error, _error.typeName()); return false; } @@ -1086,15 +1088,17 @@ bool CommandLineInterface::assemble( return false; } } + for (auto const& sourceAndStack: assemblyStacks) { auto const& stack = sourceAndStack.second; + auto scannerFromSourceName = [&](string const&) -> Scanner const& { return stack.scanner(); }; + SourceReferenceFormatter formatter(cerr, scannerFromSourceName); + for (auto const& error: stack.errors()) - SourceReferenceFormatter::printExceptionInformation( - cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](string const&) -> Scanner const& { return stack.scanner(); } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); if (!Error::containsOnlyWarnings(stack.errors())) successful = false; diff --git a/std/StandardToken.sol b/std/StandardToken.sol index 2986cb56..1b218d67 100644 --- a/std/StandardToken.sol +++ b/std/StandardToken.sol @@ -40,7 +40,7 @@ contract StandardToken is Token { if (balance[_from] >= _value && balance[_to] + _value >= balance[_to]) { balance[_from] -= _value; balance[_to] += _value; - Transfer(_from, _to, _value); + emit Transfer(_from, _to, _value); return true; } else { return false; @@ -49,7 +49,7 @@ contract StandardToken is Token { function approve(address _spender, uint256 _value) public returns (bool success) { m_allowance[msg.sender][_spender] = _value; - Approval(msg.sender, _spender, _value); + emit Approval(msg.sender, _spender, _value); return true; } diff --git a/test/Metadata.cpp b/test/Metadata.cpp index e4de0a6b..1ebfd468 100644 --- a/test/Metadata.cpp +++ b/test/Metadata.cpp @@ -50,7 +50,7 @@ string bytecodeSansMetadata(string const& _bytecode) bool isValidMetadata(string const& _metadata) { Json::Value metadata; - if (!Json::Reader().parse(_metadata, metadata, false)) + if (!jsonParseStrict(_metadata, metadata)) return false; if ( diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 60aafc85..69c75cee 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -23,8 +23,7 @@ #include <libdevcore/CommonData.h> -#include <json/reader.h> -#include <json/writer.h> +#include <libdevcore/JSON.h> #include <string> #include <stdio.h> @@ -216,7 +215,7 @@ string RPCSession::personal_newAccount(string const& _password) void RPCSession::test_setChainParams(vector<string> const& _accounts) { - static std::string const c_configString = R"( + static string const c_configString = R"( { "sealEngine": "NoProof", "params": { @@ -249,10 +248,10 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts) )"; Json::Value config; - BOOST_REQUIRE(Json::Reader().parse(c_configString, config)); + BOOST_REQUIRE(jsonParseStrict(c_configString, config)); for (auto const& account: _accounts) config["accounts"][account]["wei"] = "0x100000000000000000000000000000000000000000"; - test_setChainParams(Json::FastWriter().write(config)); + test_setChainParams(jsonCompactPrint(config)); } void RPCSession::test_setChainParams(string const& _config) @@ -328,7 +327,7 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& BOOST_TEST_MESSAGE("Reply: " + reply); Json::Value result; - BOOST_REQUIRE(Json::Reader().parse(reply, result, false)); + BOOST_REQUIRE(jsonParseStrict(reply, result)); if (result.isMember("error")) { @@ -371,6 +370,5 @@ string RPCSession::TransactionData::toJson() const json["gasprice"] = gasPrice; json["value"] = value; json["data"] = data; - return Json::FastWriter().write(json); - + return jsonCompactPrint(json); } diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index a249b601..e86e0ad4 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -83,6 +83,21 @@ function compileWithoutWarning() test -z "$output" -a "$failed" -eq 0 } +printTask "Testing unknown options..." +( + set +e + output=$("$SOLC" --allow=test 2>&1) + failed=$? + set -e + + if [ "$output" == "unrecognised option '--allow=test'" ] && [ $failed -ne 0 ] ; then + echo "Passed" + else + printError "Incorrect response to unknown options: $STDERR" + exit 1 + fi +) + printTask "Compiling various other contracts and libraries..." ( cd "$REPO_ROOT"/test/compilationTests/ @@ -172,4 +187,4 @@ TMPDIR=$(mktemp -d) done ) rm -rf "$TMPDIR" -echo "Done." +echo "Commandline tests successful." diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp index c9c744af..5e4991e2 100644 --- a/test/contracts/AuctionRegistrar.cpp +++ b/test/contracts/AuctionRegistrar.cpp @@ -220,13 +220,8 @@ protected: void deployRegistrar() { if (!s_compiledRegistrar) - { - m_compiler.reset(false); - m_compiler.addSource("", registrarCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode)); - } + s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "GlobalRegistrar"))); + sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 8327999d..a3a27c37 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -132,13 +132,8 @@ protected: void deployRegistrar() { if (!s_compiledRegistrar) - { - m_compiler.reset(false); - m_compiler.addSource("", registrarCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode)); - } + s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "FixedFeeRegistrar"))); + sendMessage(*s_compiledRegistrar, true); BOOST_REQUIRE(!m_output.empty()); } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 90334ad6..1031e8f1 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -447,13 +447,8 @@ protected: ) { if (!s_compiledWallet) - { - m_compiler.reset(false); - m_compiler.addSource("", walletCode); - m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); - BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed"); - s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode)); - } + s_compiledWallet.reset(new bytes(compileContract(walletCode, "Wallet"))); + bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners); sendMessage(*s_compiledWallet + args, true, _value); BOOST_REQUIRE(!m_output.empty()); diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 578e63a4..45738baa 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -18,11 +18,12 @@ * Executable for use with AFL <http://lcamtuf.coredump.cx/afl>. */ +#include <libdevcore/CommonIO.h> #include <libevmasm/Assembly.h> #include <libevmasm/ConstantOptimiser.h> #include <libsolc/libsolc.h> -#include <json/json.h> +#include <libdevcore/JSON.h> #include <boost/program_options.hpp> @@ -82,26 +83,15 @@ void testConstantOptimizer() } } -string readInput() -{ - string input; - while (!cin.eof()) - { - string s; - getline(cin, s); - input += s + '\n'; - } - return input; -} - void testStandardCompiler() { if (!quiet) cout << "Testing compiler via JSON interface." << endl; - string input = readInput(); + string input = readStandardInput(); + string outputString(compileStandard(input.c_str(), NULL)); Json::Value output; - if (!Json::Reader().parse(outputString, output)) + if (!jsonParseStrict(outputString, output)) { cout << "Compiler produced invalid JSON output." << endl; abort(); @@ -125,11 +115,11 @@ void testCompiler(bool optimize) { if (!quiet) cout << "Testing compiler " << (optimize ? "with" : "without") << " optimizer." << endl; - string input = readInput(); + string input = readStandardInput(); string outputString(compileJSON(input.c_str(), optimize)); Json::Value outputJson; - if (!Json::Reader().parse(outputString, outputJson)) + if (!jsonParseStrict(outputString, outputJson)) { cout << "Compiler produced invalid JSON output." << endl; abort(); diff --git a/test/libdevcore/JSON.cpp b/test/libdevcore/JSON.cpp new file mode 100644 index 00000000..39d71b42 --- /dev/null +++ b/test/libdevcore/JSON.cpp @@ -0,0 +1,151 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2018 + * Unit tests for JSON.h. + */ + +#include <libdevcore/JSON.h> + +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(JsonTest) + +BOOST_AUTO_TEST_CASE(json_pretty_print) +{ + Json::Value json; + Json::Value jsonChild; + + jsonChild["3.1"] = "3.1"; + jsonChild["3.2"] = 2; + json["1"] = 1; + json["2"] = "2"; + json["3"] = jsonChild; + + BOOST_CHECK( + "{\n" + " \"1\" : 1,\n" + " \"2\" : \"2\",\n" + " \"3\" : \n" + " {\n" + " \"3.1\" : \"3.1\",\n" + " \"3.2\" : 2\n" + " }\n" + "}" == jsonPrettyPrint(json)); +} + +BOOST_AUTO_TEST_CASE(json_compact_print) +{ + Json::Value json; + Json::Value jsonChild; + + jsonChild["3.1"] = "3.1"; + jsonChild["3.2"] = 2; + json["1"] = 1; + json["2"] = "2"; + json["3"] = jsonChild; + + BOOST_CHECK("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}" == jsonCompactPrint(json)); +} + +BOOST_AUTO_TEST_CASE(parse_json_not_strict) +{ + Json::Value json; + std::string errors; + + // just parse a valid json input + BOOST_CHECK(jsonParse("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); + BOOST_CHECK(json["1"] == 1); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 2); + + // trailing garbage is allowed here + BOOST_CHECK(jsonParse("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); + BOOST_CHECK(json["1"] == 2); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 3); + + // comments are allowed + BOOST_CHECK(jsonParse( + "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":4}}", json, &errors + )); + BOOST_CHECK(json["1"] == 3); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 4); + + // root element other than object or array is allowed + BOOST_CHECK(jsonParse("[]", json, &errors)); + BOOST_CHECK(jsonParse("{}", json, &errors)); + BOOST_CHECK(jsonParse("1", json, &errors)); + BOOST_CHECK(json == 1); + BOOST_CHECK(jsonParse("\"hello\"", json, &errors)); + BOOST_CHECK(json == "hello"); + + // non-UTF-8 escapes allowed + BOOST_CHECK(jsonParse("[ \"\x80\xec\x80\" ]", json, &errors)); + BOOST_CHECK(json[0] == "\x80\xec\x80"); +} + +BOOST_AUTO_TEST_CASE(parse_json_strict) +{ + Json::Value json; + std::string errors; + + // just parse a valid json input + BOOST_CHECK(jsonParseStrict("{\"1\":1,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":2}}", json, &errors)); + BOOST_CHECK(json["1"] == 1); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 2); + + // trailing garbage is not allowed in strict-mode + BOOST_CHECK(!jsonParseStrict("{\"1\":2,\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":3}}}}}}}}}}", json, &errors)); + + // comments are allowed in strict-mode? - that's strange... + BOOST_CHECK(jsonParseStrict( + "{\"1\":3, // awesome comment\n\"2\":\"2\",\"3\":{\"3.1\":\"3.1\",\"3.2\":5}}", json, &errors + )); + BOOST_CHECK(json["1"] == 3); + BOOST_CHECK(json["2"] == "2"); + BOOST_CHECK(json["3"]["3.1"] == "3.1"); + BOOST_CHECK(json["3"]["3.2"] == 5); + + // root element can only be object or array + BOOST_CHECK(jsonParseStrict("[]", json, &errors)); + BOOST_CHECK(jsonParseStrict("{}", json, &errors)); + BOOST_CHECK(!jsonParseStrict("1", json, &errors)); + BOOST_CHECK(!jsonParseStrict("\"hello\"", json, &errors)); + + // non-UTF-8 escapes allowed?? + BOOST_CHECK(jsonParseStrict("[ \"\x80\xec\x80\" ]", json, &errors)); + BOOST_CHECK(json[0] == "\x80\xec\x80"); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp index e1ab8215..7053a68d 100644 --- a/test/libjulia/Common.cpp +++ b/test/libjulia/Common.cpp @@ -40,12 +40,12 @@ using namespace dev::solidity; void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) { + SourceReferenceFormatter formatter(cout, [&](std::string const&) -> Scanner const& { return _scanner; }); + for (auto const& error: _errors) - SourceReferenceFormatter::printExceptionInformation( - cout, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const&) -> Scanner const& { return _scanner; } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); } @@ -56,7 +56,7 @@ pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test: ErrorList errors; ErrorReporter errorReporter(errors); auto scanner = make_shared<Scanner>(CharStream(_source), ""); - auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner, false); if (parserResult) { BOOST_REQUIRE(errorReporter.errors().empty()); diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index a8a41b3c..ff9474c1 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -52,7 +52,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter) try { auto scanner = make_shared<Scanner>(CharStream(_source)); - auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::IULIA).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::IULIA).parse(scanner, false); if (parserResult) { assembly::AsmAnalysisInfo analysisInfo; @@ -228,6 +228,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_ERROR("{ let x:u256 := .1:u256 }", ParserError, "Invalid number literal."); CHECK_ERROR("{ let x:u256 := 1e5:u256 }", ParserError, "Invalid number literal."); CHECK_ERROR("{ let x:u256 := 67.235:u256 }", ParserError, "Invalid number literal."); + CHECK_ERROR("{ let x:u256 := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff:u256 }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(builtin_types) diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 31165922..a165f7a9 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -237,16 +237,31 @@ BOOST_AUTO_TEST_CASE(documentation) " and has a line-breaking comment.*/" "contract C {}" ); + c.addSource("c", + "contract C {" + " /** Some comment on Evt.*/ event Evt();" + " /** Some comment on mod.*/ modifier mod() { _; }" + " /** Some comment on fn.*/ function fn() public {}" + "}" + ); c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 0; sourceIndices["b"] = 1; + sourceIndices["c"] = 2; Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"]; BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b")); Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"]; BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); + Json::Value astJsonC = ASTJsonConverter(true, sourceIndices).toJson(c.ast("c")); + Json::Value documentationC0 = astJsonC["children"][0]["children"][0]["attributes"]["documentation"]; + Json::Value documentationC1 = astJsonC["children"][0]["children"][1]["attributes"]["documentation"]; + Json::Value documentationC2 = astJsonC["children"][0]["children"][2]["attributes"]["documentation"]; + BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); + BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); + BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); //same tests for non-legacy mode astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); documentationA = astJsonA["nodes"][0]["documentation"]; @@ -254,7 +269,13 @@ BOOST_AUTO_TEST_CASE(documentation) astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b")); documentationB = astJsonB["nodes"][0]["documentation"]; BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); - + astJsonC = ASTJsonConverter(false, sourceIndices).toJson(c.ast("c")); + documentationC0 = astJsonC["nodes"][0]["nodes"][0]["documentation"]; + documentationC1 = astJsonC["nodes"][0]["nodes"][1]["documentation"]; + documentationC2 = astJsonC["nodes"][0]["nodes"][2]["documentation"]; + BOOST_CHECK_EQUAL(documentationC0, "Some comment on Evt."); + BOOST_CHECK_EQUAL(documentationC1, "Some comment on mod."); + BOOST_CHECK_EQUAL(documentationC2, "Some comment on fn."); } diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index 86e8201b..9d3409dd 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -136,8 +136,10 @@ BOOST_AUTO_TEST_CASE(non_overlapping_filtered_costs) { BOOST_CHECK_MESSAGE(false, "Source locations should not overlap!"); auto scannerFromSource = [&](string const& _sourceName) -> Scanner const& { return m_compiler.scanner(_sourceName); }; - SourceReferenceFormatter::printSourceLocation(cout, &first->first->location(), scannerFromSource); - SourceReferenceFormatter::printSourceLocation(cout, &second->first->location(), scannerFromSource); + SourceReferenceFormatter formatter(cout, scannerFromSource); + + formatter.printSourceLocation(&first->first->location()); + formatter.printSourceLocation(&second->first->location()); } } } diff --git a/test/libsolidity/Imports.cpp b/test/libsolidity/Imports.cpp index 03287b28..dc1174f4 100644 --- a/test/libsolidity/Imports.cpp +++ b/test/libsolidity/Imports.cpp @@ -266,7 +266,28 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_with_multiple_imports) } } - +BOOST_AUTO_TEST_CASE(shadowing_builtins_with_alias) +{ + CompilerStack c; + c.addSource("B.sol", "contract C {} pragma solidity >=0.0;"); + c.addSource("b", R"( + pragma solidity >=0.0; + import {C as msg} from "B.sol"; + )"); + BOOST_CHECK(c.compile()); + auto numErrors = c.errors().size(); + // Sometimes we get the prerelease warning, sometimes not. + BOOST_CHECK(1 <= numErrors && numErrors <= 2); + for (auto const& e: c.errors()) + { + string const* msg = e->comment(); + BOOST_REQUIRE(msg); + BOOST_CHECK( + msg->find("pre-release") != string::npos || + msg->find("shadows a builtin symbol") != string::npos + ); + } +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index b09eb261..ea120657 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -168,6 +168,11 @@ BOOST_AUTO_TEST_CASE(smoke_test) BOOST_CHECK(successParse("{ }")); } +BOOST_AUTO_TEST_CASE(surplus_input) +{ + CHECK_PARSE_ERROR("{ } { }", ParserError, "Expected token EOS"); +} + BOOST_AUTO_TEST_CASE(simple_instructions) { BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }")); @@ -385,6 +390,7 @@ BOOST_AUTO_TEST_CASE(number_literals) CHECK_PARSE_ERROR("{ let x := .1 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 1e5 }", ParserError, "Invalid number literal."); CHECK_PARSE_ERROR("{ let x := 67.235 }", ParserError, "Invalid number literal."); + CHECK_STRICT_ERROR("{ let x := 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff }", TypeError, "Number literal too large (> 256 bits)"); } BOOST_AUTO_TEST_CASE(function_definitions) @@ -768,6 +774,20 @@ BOOST_AUTO_TEST_CASE(create2) BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }")); } +BOOST_AUTO_TEST_CASE(shift) +{ + BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }")); + BOOST_CHECK(successAssemble("{ pop(sar(10, 32)) }")); +} + +BOOST_AUTO_TEST_CASE(shift_constantinople_warning) +{ + CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork"); + CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork"); +} + BOOST_AUTO_TEST_CASE(jump_warning) { CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions"); diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 0c904c77..285c5604 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -44,7 +44,7 @@ Json::Value compileSingle(string const& _input) { string output(compileJSON(_input.c_str(), dev::test::Options::get().optimize)); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -56,7 +56,7 @@ Json::Value compileMulti(string const& _input, bool _callback) compileJSONMulti(_input.c_str(), dev::test::Options::get().optimize) ); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -64,7 +64,7 @@ Json::Value compile(string const& _input) { string output(compileStandard(_input.c_str(), NULL)); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp index efe8faff..47cf1d3d 100644 --- a/test/libsolidity/Metadata.cpp +++ b/test/libsolidity/Metadata.cpp @@ -23,6 +23,7 @@ #include "../TestHelper.h" #include <libsolidity/interface/CompilerStack.h> #include <libdevcore/SwarmHash.h> +#include <libdevcore/JSON.h> namespace dev { @@ -111,7 +112,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources) std::string const& serialisedMetadata = compilerStack.metadata("A"); BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; - BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + BOOST_REQUIRE(jsonParseStrict(serialisedMetadata, metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 1); BOOST_CHECK(metadata["sources"].isMember("A")); @@ -149,7 +150,7 @@ BOOST_AUTO_TEST_CASE(metadata_relevant_sources_imports) std::string const& serialisedMetadata = compilerStack.metadata("C"); BOOST_CHECK(dev::test::isValidMetadata(serialisedMetadata)); Json::Value metadata; - BOOST_REQUIRE(Json::Reader().parse(serialisedMetadata, metadata, false)); + BOOST_REQUIRE(jsonParseStrict(serialisedMetadata, metadata)); BOOST_CHECK_EQUAL(metadata["sources"].size(), 3); BOOST_CHECK(metadata["sources"].isMember("A")); diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 8c955292..12b5f439 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -35,12 +35,6 @@ namespace test class SMTCheckerFramework: public AnalysisFramework { -public: - SMTCheckerFramework() - { - m_warningsToFilter.push_back("Experimental features are turned on."); - } - protected: virtual std::pair<SourceUnit const*, ErrorList> parseAnalyseAndReturnError( @@ -103,6 +97,7 @@ BOOST_AUTO_TEST_CASE(warn_on_struct) } )"; CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "Experimental feature", "Assertion checker does not yet implement this expression.", "Assertion checker does not yet support the type of this variable." })); @@ -471,7 +466,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 3); @@ -482,7 +478,8 @@ BOOST_AUTO_TEST_CASE(for_loop) text = R"( contract C { function f(uint x) public pure { - for (uint y = 2; x < 10; ) { + uint y; + for (y = 2; x < 10; ) { y = 3; } assert(y == 2); diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 26bfb6d0..e242508a 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -26,7 +26,7 @@ #include <libdevcore/Exceptions.h> #include <libdevcore/SwarmHash.h> -#include <json/json.h> +#include <libdevcore/JSON.h> namespace dev { @@ -48,7 +48,7 @@ public: Json::Value generatedInterface = m_compilerStack.contractABI(m_compilerStack.lastContractName()); Json::Value expectedInterface; - BOOST_REQUIRE(m_reader.parse(_expectedInterfaceString, expectedInterface)); + BOOST_REQUIRE(jsonParseStrict(_expectedInterfaceString, expectedInterface)); BOOST_CHECK_MESSAGE( expectedInterface == generatedInterface, "Expected:\n" << expectedInterface.toStyledString() << @@ -58,7 +58,6 @@ public: protected: CompilerStack m_compilerStack; - Json::Reader m_reader; }; BOOST_FIXTURE_TEST_SUITE(SolidityABIJSON, JSONInterfaceChecker) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 0611e71d..c352a2c2 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -284,6 +284,54 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions) ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2))); } +BOOST_AUTO_TEST_CASE(C99_scoping_activation) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public returns (uint) { + uint x = 7; + { + x = 3; // This should still assign to the outer variable + uint x; + x = 4; // This should assign to the new one + } + return x; + } + function g() pure public returns (uint x) { + x = 7; + { + x = 3; + uint x; + return x; // This returns the new variable, i.e. 0 + } + } + function h() pure public returns (uint x, uint a, uint b) { + x = 7; + { + x = 3; + a = x; // This should read from the outer + uint x = 4; + b = x; + } + } + function i() pure public returns (uint x, uint a) { + x = 7; + { + x = 3; + uint x = x; // This should read from the outer and assign to the inner + a = x; + } + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(0)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(3, 3, 4)); + ABI_CHECK(callContractFunction("i()"), encodeArgs(3, 3)); +} + BOOST_AUTO_TEST_CASE(recursive_calls) { char const* sourceCode = R"( @@ -2967,6 +3015,29 @@ BOOST_AUTO_TEST_CASE(event) } } +BOOST_AUTO_TEST_CASE(event_emit) +{ + char const* sourceCode = R"( + contract ClientReceipt { + event Deposit(address indexed _from, bytes32 indexed _id, uint _value); + function deposit(bytes32 _id) payable { + emit Deposit(msg.sender, _id, msg.value); + } + } + )"; + compileAndRun(sourceCode); + u256 value(18); + u256 id(0x1234); + callContractFunctionWithValue("deposit(bytes32)", value, id); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK_EQUAL(h256(m_logs[0].data), h256(u256(value))); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 3); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,bytes32,uint256)"))); + BOOST_CHECK_EQUAL(m_logs[0].topics[1], h256(m_sender, h256::AlignRight)); + BOOST_CHECK_EQUAL(m_logs[0].topics[2], h256(id)); +} + BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( @@ -3009,6 +3080,28 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); } +BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit) +{ + char const* sourceCode = R"( + contract A { + event x(); + } + contract B is A { + function f() returns (uint) { + emit A.x(); + return 1; + } + } + )"; + compileAndRun(sourceCode); + callContractFunction("f()"); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("x()"))); +} + BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( @@ -3107,6 +3200,58 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); } +BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit) +{ + char const* sourceCode = R"( + contract A { + event Deposit(); + } + + contract B { + event Deposit(address _addr); + } + + contract ClientReceipt is A, B { + event Deposit(address _addr, uint _amount); + function deposit() returns (uint) { + emit Deposit(); + return 1; + } + function deposit(address _addr) returns (uint) { + emit Deposit(_addr); + return 1; + } + function deposit(address _addr, uint _amount) returns (uint) { + emit Deposit(_addr, _amount); + return 1; + } + } + )"; + u160 const c_loggedAddress = m_contractAddress; + + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data.empty()); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit()"))); + + ABI_CHECK(callContractFunction("deposit(address)", c_loggedAddress), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address)"))); + + ABI_CHECK(callContractFunction("deposit(address,uint256)", c_loggedAddress, u256(100)), encodeArgs(u256(1))); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(c_loggedAddress, 100)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Deposit(address,uint256)"))); +} + BOOST_AUTO_TEST_CASE(event_anonymous) { char const* sourceCode = R"( @@ -3565,8 +3710,8 @@ BOOST_AUTO_TEST_CASE(library_call_protection) } )"; compileAndRun(sourceCode, 0, "Lib"); - ABI_CHECK(callContractFunction("np(Lib.S storage)"), encodeArgs()); - ABI_CHECK(callContractFunction("v(Lib.S storage)"), encodeArgs(u160(m_sender))); + ABI_CHECK(callContractFunction("np(Lib.S storage)", 0), encodeArgs()); + ABI_CHECK(callContractFunction("v(Lib.S storage)", 0), encodeArgs(u160(m_sender))); ABI_CHECK(callContractFunction("pu()"), encodeArgs(2)); compileAndRun(sourceCode, 0, "Test", bytes(), map<string, Address>{{"Lib", m_contractAddress}}); ABI_CHECK(callContractFunction("s()"), encodeArgs(0)); @@ -7459,6 +7604,33 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod) ABI_CHECK(callContractFunction("test()"), encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(addmod_mulmod_zero) +{ + char const* sourceCode = R"( + contract C { + function f() pure returns (uint) { + addmod(1, 2, 0); + return 2; + } + function g() pure returns (uint) { + mulmod(1, 2, 0); + return 2; + } + function h() pure returns (uint) { + mulmod(0, 1, 2); + mulmod(1, 0, 2); + addmod(0, 1, 2); + addmod(1, 0, 2); + return 2; + } + } + )"; + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f()"), encodeArgs()); + ABI_CHECK(callContractFunction("g()"), encodeArgs()); + ABI_CHECK(callContractFunction("h()"), encodeArgs(2)); +} + BOOST_AUTO_TEST_CASE(divisiod_by_zero) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index b0daaba9..f562721d 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -52,6 +52,17 @@ public: std::map<std::string, dev::test::Address> const& _libraryAddresses = std::map<std::string, dev::test::Address>() ) override { + bytes bytecode = compileContract(_sourceCode, _contractName, _libraryAddresses); + sendMessage(bytecode + _arguments, true, _value); + return m_output; + } + + bytes compileContract( + std::string const& _sourceCode, + std::string const& _contractName = "", + std::map<std::string, dev::test::Address> const& _libraryAddresses = std::map<std::string, dev::test::Address>() + ) + { // Silence compiler version warning std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode; m_compiler.reset(false); @@ -60,19 +71,19 @@ public: m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns); if (!m_compiler.compile()) { + auto scannerFromSourceName = [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); }; + SourceReferenceFormatter formatter(std::cerr, scannerFromSourceName); + for (auto const& error: m_compiler.errors()) - SourceReferenceFormatter::printExceptionInformation( - std::cerr, + formatter.printExceptionInformation( *error, - (error->type() == Error::Type::Warning) ? "Warning" : "Error", - [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + (error->type() == Error::Type::Warning) ? "Warning" : "Error" ); BOOST_ERROR("Compiling contract failed"); } eth::LinkerObject obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName); BOOST_REQUIRE(obj.linkReferences.empty()); - sendMessage(obj.bytecode + _arguments, true, _value); - return m_output; + return obj.bytecode; } protected: diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 67747386..e2a0c3cd 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -322,10 +322,10 @@ BOOST_AUTO_TEST_CASE(arithmetics) { char const* sourceCode = R"( contract test { - function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } + function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x1, byte(Instruction::PUSH1), 0x2, byte(Instruction::PUSH1), 0x3, @@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::PUSH1), 0x6, byte(Instruction::PUSH1), 0x7, byte(Instruction::PUSH1), 0x8, - byte(Instruction::DUP10), + byte(Instruction::DUP9), byte(Instruction::XOR), byte(Instruction::AND), byte(Instruction::OR), @@ -364,13 +364,13 @@ BOOST_AUTO_TEST_CASE(unary_operators) { char const* sourceCode = R"( contract test { - function f(int y) { var x = !(~+- y == 2); } + function f(int y) { !(~+- y == 2); } } )"; - bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}}); + bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}}); bytes expectation({byte(Instruction::PUSH1), 0x2, - byte(Instruction::DUP3), + byte(Instruction::DUP2), byte(Instruction::PUSH1), 0x0, byte(Instruction::SUB), byte(Instruction::NOT), @@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) { char const* sourceCode = R"( contract test { - function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); } + function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); } } )"; bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}}); @@ -426,7 +426,10 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec) byte(Instruction::POP), // second ++ // Stack here: a x a^(a+2)^(a+2) byte(Instruction::DUP3), // will change - byte(Instruction::XOR)}); + byte(Instruction::XOR), + byte(Instruction::SWAP1), + byte(Instruction::POP), + byte(Instruction::DUP1)}); // Stack here: a x a^(a+2)^(a+2)^a BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 315c7c5f..8c2d853c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -76,15 +76,236 @@ BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration) { - char const* text = R"( + string text = R"( contract test { - function f() public { + function f() pure public { uint256 x; if (true) { uint256 x; } } } )"; - CHECK_ERROR(text, DeclarationError, "Identifier already declared."); + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + uint256 x; + if (true) { uint256 x; } + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "This declaration shadows an existing declaration.", + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope) +{ + string text = R"( + contract test { + function f() pure public { + { uint x; } + { uint x; } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + { uint x; } + { uint x; } + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation) +{ + string text = R"( + contract test { + function f() pure public { + { uint x; } + uint x; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Identifier already declared"); +} + +BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050) +{ + string text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + { uint x; } + uint x; + } + } + )"; + CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{ + "Experimental features", + "Unused local variable", + "Unused local variable" + })); +} +BOOST_AUTO_TEST_CASE(scoping_old) +{ + char const* text = R"( + contract test { + function f() pure public { + x = 4; + uint256 x = 2; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() public { + { + uint256 x; + } + x = 2; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_activation_old) +{ + char const* text = R"( + contract test { + function f() pure public { + x = 3; + uint x; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_activation) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + x = 3; + uint x; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_self_use) +{ + char const* text = R"( + contract test { + function f() pure public { + uint a = a; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(scoping_self_use_050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + uint a = a; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_for) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(scoping_for2) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++) + x = 2; + } + } + )"; + CHECK_WARNING(text, "Experimental features"); +} + +BOOST_AUTO_TEST_CASE(scoping_for3) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + for (uint x = 0; x < 10; x ++){ + x = 2; + } + x = 4; + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); +} + +BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract test { + function f() pure public { + for (;; y++){ + uint y = 3; + } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier"); } BOOST_AUTO_TEST_CASE(name_shadowing) @@ -1004,7 +1225,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation) { char const* text = R"( contract B { - function f() mod1(2, true) mod2("0123456") public { } + function f() mod1(2, true) mod2("0123456") pure public { } modifier mod1(uint a, bool b) { if (b) _; } modifier mod2(bytes7 a) { while (a == "1234567") _; } } @@ -1039,11 +1260,23 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) { char const* text = R"( contract B { - function f() mod(x) public { uint x = 7; } + function f() mod(x) pure public { uint x = 7; } modifier mod(uint a) { if (a > 0) _; } } )"; - CHECK_SUCCESS(text); + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract B { + function f() mod(x) pure public { uint x = 7; } + modifier mod(uint a) { if (a > 0) _; } + } + )"; + CHECK_ERROR(text, DeclarationError, "Undeclared identifier."); } BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) @@ -3021,6 +3254,20 @@ BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable) CHECK_WARNING(sourceCode, "Uninitialized storage pointer"); } +BOOST_AUTO_TEST_CASE(uninitialized_mapping_array_variable_050) +{ + char const* sourceCode = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + mapping(uint => uint)[] storage x; + x; + } + } + )"; + CHECK_ERROR(sourceCode, DeclarationError, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(no_delete_on_storage_pointers) { char const* sourceCode = R"( @@ -3320,6 +3567,24 @@ BOOST_AUTO_TEST_CASE(non_initialized_references) CHECK_WARNING(text, "Uninitialized storage pointer"); } +BOOST_AUTO_TEST_CASE(non_initialized_references_050) +{ + char const* text = R"( + pragma experimental "v0.5.0"; + contract c + { + struct s { + uint a; + } + function f() public { + s storage x; + } + } + )"; + + CHECK_ERROR(text, DeclarationError, "Uninitialized storage pointer"); +} + BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) { char const* text = R"( @@ -5744,6 +6009,21 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset) +{ + char const* text = R"( + contract test { + uint constant x = 2; + function f() pure public { + assembly { + let r := x_offset + } + } + } + )"; + CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly."); +} + BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables) { char const* text = R"( @@ -6285,7 +6565,16 @@ BOOST_AUTO_TEST_CASE(warn_about_throw) } } )"; - CHECK_WARNING(text, "\"throw\" is deprecated"); + CHECK_WARNING(text, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure public { + throw; + } + } + )"; + CHECK_ERROR(text, SyntaxError, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\""); } BOOST_AUTO_TEST_CASE(bare_revert) @@ -7685,7 +7974,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) char const* text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.balance; } } @@ -7694,7 +7983,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.transfer; } } @@ -7703,7 +7992,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.send; } } @@ -7712,7 +8001,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.call; } } @@ -7721,7 +8010,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.callcode; } } @@ -7730,7 +8019,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) text = R"( pragma experimental "v0.5.0"; contract C { - function f() { + function f() public { this.delegatecall; } } @@ -7738,6 +8027,99 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract) CHECK_ERROR(text, TypeError, "Member \"delegatecall\" not found or not visible after argument-dependent lookup in contract"); } +BOOST_AUTO_TEST_CASE(emit_events) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + emit e(); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + event e(uint a, string b); + function f() public { + emit e(2, "abc"); + emit e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract A { event e(uint a, string b); } + contract C is A { + function f() public { + emit A.e(2, "abc"); + emit A.e({b: "abc", a: 8}); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(old_style_events_050) +{ + char const* text = R"( + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_WARNING(text, "without \"emit\" prefix"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + event e(); + function f() public { + e(); + } + } + )"; + CHECK_ERROR(text, TypeError, "have to be prefixed"); +} + +BOOST_AUTO_TEST_CASE(getter_is_memory_type) +{ + char const* text = R"( + contract C { + struct S { string m; } + string[] public x; + S[] public y; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + // Check that the getters return a memory strings, not a storage strings. + ContractDefinition const& c = dynamic_cast<ContractDefinition const&>(*m_compiler.ast("").nodes().at(1)); + BOOST_CHECK(c.interfaceFunctions().size() == 2); + for (auto const& f: c.interfaceFunctions()) + { + auto const& retType = f.second->returnParameterTypes().at(0); + BOOST_CHECK(retType->dataStoredIn(DataLocation::Memory)); + } +} + +BOOST_AUTO_TEST_CASE(require_visibility_specifiers) +{ + char const* text = R"( + contract C { + function f() pure { } + } + )"; + CHECK_WARNING(text, "No visibility specified. Defaulting to"); + text = R"( + pragma experimental "v0.5.0"; + contract C { + function f() pure { } + } + )"; + CHECK_ERROR(text, SyntaxError, "No visibility specified."); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index fb09451f..e8906bb9 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -22,7 +22,7 @@ #include "../TestHelper.h" #include <string> -#include <json/json.h> +#include <libdevcore/JSON.h> #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/Exceptions.h> #include <libdevcore/Exceptions.h> @@ -55,7 +55,7 @@ public: else generatedDocumentation = m_compilerStack.natspecDev(m_compilerStack.lastContractName()); Json::Value expectedDocumentation; - m_reader.parse(_expectedDocumentationString, expectedDocumentation); + jsonParseStrict(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( expectedDocumentation == generatedDocumentation, "Expected:\n" << expectedDocumentation.toStyledString() << @@ -73,7 +73,6 @@ public: private: CompilerStack m_compilerStack; - Json::Reader m_reader; }; BOOST_FIXTURE_TEST_SUITE(SolidityNatspecJSON, DocumentationChecker) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index bd635c33..33039ca9 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(constant_folding_both_sides) } })"; compileBothVersions(sourceCode); - compareVersions("f(uint256)"); + compareVersions("f(uint256)", 7); } BOOST_AUTO_TEST_CASE(storage_access) @@ -190,7 +190,7 @@ BOOST_AUTO_TEST_CASE(storage_access) } )"; compileBothVersions(sourceCode); - compareVersions("f(uint256)"); + compareVersions("f(uint256)", 7); } BOOST_AUTO_TEST_CASE(array_copy) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 861e6408..b7097d0f 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1708,6 +1708,19 @@ BOOST_AUTO_TEST_CASE(newInvalidTypeName) CHECK_PARSE_ERROR(text, "Expected explicit type name"); } +BOOST_AUTO_TEST_CASE(emitWithoutEvent) +{ + char const* text = R"( + contract C { + event A(); + function f() { + emit A; + } + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 9f385a04..bc9f2fe1 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -88,6 +88,21 @@ BOOST_AUTO_TEST_CASE(storage_layout_arrays) BOOST_CHECK(ArrayType(DataLocation::Storage, make_shared<FixedBytesType>(32), 9).storageSize() == 9); } +BOOST_AUTO_TEST_CASE(type_escaping) +{ + BOOST_CHECK_EQUAL(Type::escapeIdentifier("("), "$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(")"), "_$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier(","), "_$_"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("$"), "$$$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("()"), "$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,)"), "$__$__$"); + BOOST_CHECK_EQUAL(Type::escapeIdentifier("(,$,)"), "$__$_$$$_$__$"); + BOOST_CHECK_EQUAL( + Type::escapeIdentifier("((__(_$_$$,__($$,,,$$),$,,,)))$$,$$"), + "$_$___$__$$$_$$$$$$_$___$_$$$$$$_$__$__$_$$$$$$_$_$_$$$_$__$__$__$_$_$$$$$$$_$_$$$$$$" + ); +} + BOOST_AUTO_TEST_CASE(type_identifiers) { ASTNode::resetID(); diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index e48624e5..0bb94172 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -89,7 +89,7 @@ Json::Value compile(string const& _input) StandardCompiler compiler; string output = compiler.compile(_input); Json::Value ret; - BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + BOOST_REQUIRE(jsonParseStrict(output, ret)); return ret; } @@ -110,11 +110,11 @@ BOOST_AUTO_TEST_CASE(assume_object_input) /// Use the string interface of StandardCompiler to trigger these result = compile(""); - BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n")); result = compile("invalid"); - BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 2\n Extra non-whitespace after JSON value.\n")); result = compile("\"invalid\""); - BOOST_CHECK(containsError(result, "JSONError", "Input is not a JSON object.")); + BOOST_CHECK(containsError(result, "JSONError", "* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n")); BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); result = compile("{}"); BOOST_CHECK(!containsError(result, "JSONError", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n")); @@ -154,6 +154,61 @@ BOOST_AUTO_TEST_CASE(no_sources) BOOST_CHECK(containsError(result, "JSONError", "No input sources specified.")); } +BOOST_AUTO_TEST_CASE(no_sources_empty_object) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": {} + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "No input sources specified.")); +} + +BOOST_AUTO_TEST_CASE(no_sources_empty_array) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": [] + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(sources_is_array) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": ["aa", "bb"] + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"sources\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(unexpected_trailing_test) +{ + char const* input = R"( + { + "language": "Solidity", + "sources": { + "A": { + "content": "contract A { function f() {} }" + } + } + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "* Line 10, Column 2\n Extra non-whitespace after JSON value.\n")); +} + + BOOST_AUTO_TEST_CASE(smoke_test) { char const* input = R"( @@ -515,6 +570,139 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon) BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); } +BOOST_AUTO_TEST_CASE(libraries_invalid_top_level) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": "42" + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "\"libraries\" is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_entry) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "L": "42" + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object.")); +} + +BOOST_AUTO_TEST_CASE(libraries_invalid_hex) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000xx1" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied.")); +} + +BOOST_AUTO_TEST_CASE(libraries_various_addresses) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": 42, + "L3": "42", + "L4": "0x42", + "L5": "0x4200000000000000000000000000000000000001", + "L6": "4200000000000000000000000000000000000001" + } + } + }, + "sources": { + "empty": { + "content": "" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); +} + +BOOST_AUTO_TEST_CASE(library_linking) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "libraries": { + "library.sol": { + "L": "0x4200000000000000000000000000000000000001" + } + }, + "outputSelection": { + "fileA": { + "A": [ + "evm.bytecode" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() returns (uint) { L2.g(); return L.g(); } }" + }, + "library.sol": { + "content": "library L { function g() returns (uint) { return 1; } }" + }, + "library2.sol": { + "content": "library L2 { function g() { } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["evm"]["bytecode"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); + BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject()); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp index 3a03c877..2599ca28 100644 --- a/test/libsolidity/ViewPureChecker.cpp +++ b/test/libsolidity/ViewPureChecker.cpp @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(environment_access) BOOST_AUTO_TEST_CASE(view_error_for_050) { CHECK_ERROR( - "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view { x = 2; } }", + "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view public { x = 2; } }", TypeError, "Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable." ); |