diff options
58 files changed, 1552 insertions, 646 deletions
diff --git a/.travis.yml b/.travis.yml index c476c154..d787c795 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,27 +26,40 @@ language: cpp branches: + # We need to whitelist the branches which we want to have "push" automation. + # Pull request automation is not constrained to this set of branches. only: - develop - release - - standalone - except: - - /develop-v[0-9]/ matrix: include: + # Ubuntu 14.04 LTS "Trusty Tahr" + # https://en.wikipedia.org/wiki/List_of_Ubuntu_releases#Ubuntu_14.04_LTS_.28Trusty_Tahr.29 + # + # TravisCI doesn't directly support any new Ubuntu releases. These is + # some Docker support, which we should probably investigate, at least for + # Ubuntu 16.04 LTS "Xenial Xerus" + # See https://en.wikipedia.org/wiki/List_of_Ubuntu_releases#Ubuntu_16.04_LTS_.28Xenial_Xerus.29. - os: linux dist: trusty sudo: required compiler: gcc env: - - TRAVIS_BUILD_TYPE=RelWithDebInfo - ZIP_SUFFIX=ubuntu-trusty + + # Documentation target, which generates documentation using Phoenix / ReadTheDocs. - os: linux dist: trusty sudo: required compiler: gcc env: - - JOB=docs + - TRAVIS_DOCS=On + - TRAVIS_RELEASE=Off + - TRAVIS_TESTS=Off + + # Emscripten target, which compiles 'solc' to javascript and uploads the resulting .js + # files to https://github.com/ethereum/solc-bin. These binaries are used in Browser-Solidity + # and in other Ethereum web-based development contexts. - os: linux dist: trusty sudo: required @@ -56,30 +69,56 @@ matrix: before_install: - docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit env: - - JOB=emscripten + - TRAVIS_EMSCRIPTEN=On + - TRAVIS_INSTALL_DEPS=Off + - TRAVIS_RELEASE=Off + - TRAVIS_TESTS=Off + + # OS X Mavericks (10.9) + # https://en.wikipedia.org/wiki/OS_X_Mavericks + # - os: osx osx_image: beta-xcode6.2 env: - - TRAVIS_BUILD_TYPE=RelWithDebInfo - ZIP_SUFFIX=osx-mavericks + + # OS X Yosemite (10.10) + # https://en.wikipedia.org/wiki/OS_X_Yosemite + # - os: osx osx_image: xcode7.1 env: - - TRAVIS_BUILD_TYPE=RelWithDebInfo + # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" + # https://github.com/ethereum/solidity/issues/894 + - TRAVIS_TESTS=Off - ZIP_SUFFIX=osx-yosemite + + # OS X El Capitan (10.11) + # https://en.wikipedia.org/wiki/OS_X_El_Capitan + # - os: osx osx_image: xcode7.3 env: # The use of Debug config here ONLY for El Capitan is a workaround for "The Heisenbug" # See https://github.com/ethereum/webthree-umbrella/issues/565 - TRAVIS_BUILD_TYPE=Debug + # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" + # https://github.com/ethereum/solidity/issues/894 + - TRAVIS_TESTS=Off - ZIP_SUFFIX=osx-elcapitan + + # macOS Sierra (10.12) + # https://en.wikipedia.org/wiki/MacOS_Sierra + # - os: osx osx_image: xcode8 env: # Look like "The Heisenbug" is occurring here too, so we'll do the same workaround. # See https://travis-ci.org/ethereum/solidity/jobs/150240930 - TRAVIS_BUILD_TYPE=Debug + # Workaround for "macOS - Yosemite, El Capitan and Sierra hanging?" + # https://github.com/ethereum/solidity/issues/894 + - TRAVIS_TESTS=Off - ZIP_SUFFIX=macos-sierra git: depth: 2 @@ -93,16 +132,18 @@ cache: - jsoncpp install: - - test $JOB != default -a $JOB != docs || ./scripts/install_deps.sh + - test $TRAVIS_INSTALL_DEPS != On || ./scripts/install_deps.sh before_script: - - test $JOB != emscripten || ./scripts/build_emscripten.sh - - test $JOB != default || (mkdir -p build + - test $TRAVIS_EMSCRIPTEN != On || ./scripts/build_emscripten.sh + - test $TRAVIS_RELEASE != On || (mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE=$TRAVIS_BUILD_TYPE && make -j2 && cd .. && ./scripts/release.sh $ZIP_SUFFIX ) script: + - test $TRAVIS_DOCS != On || ./scripts/docs.sh + # There are a variety of reliability issues with the Solidity unit-tests at the time of # writing (especially on macOS), so within TravisCI we will try to run the unit-tests # up to 3 times before giving up and declaring the tests as broken. @@ -113,40 +154,68 @@ script: # itself is broken from the failure messages which we are seeing. # # More details on known issues at https://github.com/ethereum/solidity/issues/769 - - test $JOB != default || (cd $TRAVIS_BUILD_DIR && (./scripts/tests.sh || ./scripts/tests.sh || ./scripts/tests.sh) ) - - test $JOB != docs || ./scripts/docs.sh + - test $TRAVIS_TESTS != On || (cd $TRAVIS_BUILD_DIR && (./scripts/tests.sh || ./scripts/tests.sh || ./scripts/tests.sh) ) env: global: - ENCRYPTION_LABEL="6d4541b72666" - - JOB=default - -# This is the deploy target for the Emscripten build. -# It publishes the JS file which was compiled as part of the earlier 'build_emscripten.sh' -# step to https://github.com/ethereum/solc-bin/tree/gh-pages/bin. -# Both the build and deploy steps for Emscripten are only run within the Ubuntu -# configurations (not for macOS). That is controlled by conditionals within the bash -# scripts because TravisCI doesn't provide much in the way of conditional logic. + - TRAVIS_BUILD_TYPE=RelWithDebInfo + - TRAVIS_DOCS=Off + - TRAVIS_EMSCRIPTEN=Off + - TRAVIS_INSTALL_DEPS=On + - TRAVIS_RELEASE=On + - TRAVIS_TESTS=On deploy: - provider: script - script: test $JOB != emscripten || scripts/release_emscripten.sh - skip_cleanup: true - on: - branch: develop - -# This is the deploy target for the native build (Linux and macOS) -# which generates development ZIPs per commit. We are in agreement -# that this is probably noisy overkill, so will want to renable this -# functionality wrapped in some conditionals so that it only runs -# when building the 'release' branch. -# -#deploy: -# provider: releases -# api_key: -# secure: mGeDrlCbhPNQVqrk5wSqFZe/7C5HUIBWcZECJcFrEldN6ELj3a8mhDX9EWebidyFmZsf3ipKVMycJtXZHlH2kVZ0nZdRulq4bYhLiUFRaFQCHMW35dml5mxO/FPp+jhhZaylDUx+cI6AULbj8DvNFqSCfjx8qimRhJjRY4JHeG71N6g5+LU2/dA01D4Y97BUbQ5dYcmEyuEsriSpXOElIQIIv3+Q6MJNnLzxUA6EXsp4Qt3Qf3R1+EkI/RWOPbQsddpFNJBcBNOscCUFSZV3+ZK9E2RyHbPaL+Da4aJKVpgD7X1TFudq4PClMUTkg8CuJh/kvy9wkpaWyYHbLGQqu1vQ+NQ+vlTAKf8U+1xhC1IkX6nA+g4TlRksZRltRhpkBPnFoaQQGjD9eYyT1V/Htrn8Y/VGbYPBVa0GcEYXG5pDCBPz91RBpYwDcyUg9DEPNI6zYfQY8jA8xqtFwPX79Y22MDXIKhBskK00geuzh7Npy5Rnh4fLqVKMlffvYx3kwp444EFWtQ5jxbRCym2Th6EldkOM8Kble1JvixQtVb4s+DpTuwHCTrXZhwMuJpmwgRx52zyIsGrBPF3MOmdlwl+l0TD8UEJXt0JM1XSF1AROyYwXKDZQ9Qt4sd9ZKSWWaLJMEJf4kkZWYgTEI/FhOWlfshQCt8Z9S9r4fq2ywoo= -# file: $TRAVIS_BUILD_DIR/solidity-develop-$ZIP_SUFFIX.zip -# skip_cleanup: true -# on: -# repo: ethereum/solidity -# branch: develop + # This is the deploy target for the Emscripten build. + # It publishes the JS file which was compiled as part of the earlier 'build_emscripten.sh' + # step to https://github.com/ethereum/solc-bin/tree/gh-pages/bin. + # Both the build and deploy steps for Emscripten are only run within the Ubuntu + # configurations (not for macOS). That is controlled by conditionals within the bash + # scripts because TravisCI doesn't provide much in the way of conditional logic. + - provider: script + script: test $TRAVIS_EMSCRIPTEN != On || scripts/release_emscripten.sh + skip_cleanup: true + on: + branch: develop + # This is the deploy target for the native build (Linux and macOS) + # which generates ZIPs per commit. We are in agreement that + # generating ZIPs per commit for the develop branch is probably + # just noise, so we only run this deployment target on 'release'. + # + # Unlike the Appveyor GitHub Releases target, the support in TravisCI + # seemingly doesn't provide a means for passing a description, tag, etc. + # In practice, we are letting the Appveyor CI do all that stuff, and + # then this deployment flow just seems to find that most recent tag, + # and just add our Linux and macOS ZIPs into the same tag, which is + # what we want to happen. But is very accidental and brittle-looking. + # + # The 'skip_cleanup' stops the workspace being cleaned out prior to + # generation of the artifacts. Strange that we should explicitly + # need to do that, but we do. + # + # Tokens in TravisCI can be generated a few different ways. Bob had + # success using the 'travis' gem, and then using that gem to + # create/edit this .travis.yml file, and then cut-and-pasting the + # good bits back out of what it generated. The gem changes all the + # whitespace and deletes comments, so cannot be used as-is. But + # it does generate an appropriate auth token. + # + # TODO - I do not know if the api_key below which work correctly + # for ethereum/solidity. I suspect not, for the same reason as + # my auth token does not work for Appveyor. I don't have enough + # permissions to enable this myself. Christian should be able to. + # + # See https://docs.travis-ci.com/user/deployment/releases + # See https://blog.travis-ci.com/2013-01-28-token-token-token/ + # See https://github.com/ethereum/webthree-umbrella/issues/658 + # + - provider: releases + api_key: + secure: PWH37xVBCF0XiSjl+eH7XIdkrfxZXjzvqF4PiBOnD3VnFz+odrdnIwBmCeBYTHTWF8efpp8fmzWJk2UVq1JcpyZiC+SVxO8dx91W2ia1a+wKrEQuDgkUrZBkl5IQNCv0QS81DDQhliyZEaYh8wHO/7RReyMpGpw2U2u85WkFiZ+LdlHEZPfzUeh9lxQ9n8qwFL8Rja+Q05d4cQ8zaVEtofJJT4T6DUWhc3TzuxDYxOmjwg37rC9CkGSLn6VadSh8b3j5R0SZupFsAEvBL/imBLP9r9ewoo7o4p6By3jwiIgH9yNg7LM618xbffcNaYF/KtLBi9uPHfqF7hRD4PlECz+D0PR78nQItOX5HKm1QMg5kCnghRVCA0IVjpV5fiYQnMLM7dCRv34I5b3zLpa69wQ/GLYB2FViqNUfvPeiZTEeIJ2OmATlFx8AH2JoqpY1XJknWb35+vMfa8LSiJJW++SLWeV+ncC92hrvyZ1cy3trepRRZIfyYepxHifnfdWMkddQUJk5b2WS5Fy/TJLZNPeombnpvRhUC38dsYItarKeXTc6k4oADCEDZ2rgGIcEiqRxXV11Y5xHJekLDWzUs+YJNcCuL4pnAP//LOnbnH2w9rLpwhQYSl0anCd097NivAXQJXO2JI/byIYz1kiCVQWnW6EM8+72mLOklf/Qr8k= + file: $TRAVIS_BUILD_DIR/solidity-$ZIP_SUFFIX.zip + skip_cleanup: true + on: + repo: ethereum/solidity + branch: release + condition: $TRAVIS_RELEASE == On diff --git a/CMakeLists.txt b/CMakeLists.txt index 48447a49..46e09926 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.3.5") +set(PROJECT_VERSION "0.3.6") project(solidity VERSION ${PROJECT_VERSION}) # Let's find our dependencies diff --git a/Changelog.md b/Changelog.md new file mode 100644 index 00000000..8805498e --- /dev/null +++ b/Changelog.md @@ -0,0 +1,187 @@ +Features: + + * Fixed point types (in progress) + +Bugfixes: + +### 0.3.6 (2016-08-10) + +Features: + + * Formal verification: Take external effects on a contract into account. + * Type Checker: Warning about unused return value of low-level calls and send. + * Output: Source location and node id as part of AST output + * Output: Source location mappings for bytecode + * Output: Formal verification as part of json compiler output. + +Bugfixes: + + * Commandline Interface: Do not crash if input is taken from stdin. + * Scanner: Correctly support unicode escape codes in strings. + * JSON output: Fix error about relative / absolute source file names. + * JSON output: Fix error about invalid utf8 strings. + * Code Generator: Dynamic allocation of empty array caused infinite loop. + * Code Generator: Correctly calculate gas requirements for memcpy precompile. + * Optimizer: Clear known state if two code paths are joined. + +### 0.3.5 (2016-06-10) + +Features: + + * Context-dependent path remappings (different modules can use the same library in different versions) + +Bugfixes: + + * Type Checking: Dynamic return types were removed when fetching data from external calls, now they are replaced by an "unusable" type. + * Type Checking: Overrides by constructors were considered making a function non-abstract. + +### 0.3.4 (2016-05-31) + +No change outside documentation. + +### 0.3.3 (2016-05-27) + + * Allow internal library functions to be called (by "inlining") + * Fractional/rational constants (only usable with fixed point types, which are still in progress) + * Inline assembly has access to internal functions (as jump labels) + * Running `solc` without arguments on a terminal will print help. + * Bugfix: Remove some non-determinism in code generation. + * Bugfix: Corrected usage of not / bnot / iszero in inline assembly + * Bugfix: Correctly clean bytesNN types before comparison + +### 0.3.2 (2016-04-18) + + * Bugfix: Inline assembly parser: `byte` opcode was unusable + * Bugfix: Error reporting: tokens for variably-sized types were not converted to string properly + * Bugfix: Dynamic arrays of structs were not deleted correctly. + * Bugfix: Static arrays in constructor parameter list were not decoded correctly. + +### 0.3.1 (2016-03-31) + + * Inline assembly + * Bugfix: Code generation: array access with narrow types did not clean higher order bits + * Bugfix: Error reporting: error reporting with unknown source location caused a crash + +### 0.3.0 (2016-03-11) + +BREAKING CHANGES: + + * Added new keywords `assembly`, `foreign`, `fixed`, `ufixed`, `fixedNxM`, `ufixedNxM` (for various values of M and N), `timestamp` + * Number constant division does not round to integer, but to a fixed point type (e.g. `1 / 2 != 1`, but `1 / 2 == 0.5`). + * Library calls now default to use DELEGATECALL (e.g. called library functions see the same value as the calling function for `msg.value` and `msg.sender`). + * `<address>.delegatecall` as a low-level calling interface + +Bugfixes: + * Fixed a bug in the optimizer that resulted in comparisons being wrong. + + +### 0.2.2 (2016-02-17) + + * Index access for types `bytes1`, ..., `bytes32` (only read access for now). + * Bugfix: Type checker crash for wrong number of base constructor parameters. + +### 0.2.1 (2016-01-30) + + * Inline arrays, i.e. `var y = [1,x,f()];` if there is a common type for `1`, `x` and `f()`. Note that the result is always a fixed-length memory array and conversion to dynamic-length memory arrays is not yet possible. + * Import similar to ECMAScript6 import (`import "abc.sol" as d` and `import {x, y} from "abc.sol"`). + * Commandline compiler solc automatically resolves missing imports and allows for "include directories". + * Conditional: `x ? y : z` + * Bugfix: Fixed several bugs where the optimizer generated invalid code. + * Bugfix: Enums and structs were not accessible to other contracts. + * Bugfix: Fixed segfault connected to function paramater types, appeared during gas estimation. + * Bugfix: Type checker crash for wrong number of base constructor parameters. + * Bugfix: Allow function overloads with different array types. + * Bugfix: Allow assignments of type `(x) = 7`. + * Bugfix: Type `uint176` was not available. + * Bugfix: Fixed crash during type checking concerning constructor calls. + * Bugfix: Fixed crash during code generation concerning invalid accessors for struct types. + * Bugfix: Fixed crash during code generating concerning computing a hash of a struct type. + +### 0.2.0 (2015-12-02) + + * **Breaking Change**: `new ContractName.value(10)()` has to be written as `(new ContractName).value(10)()` + * Added `selfdestruct` as an alias for `suicide`. + * Allocation of memory arrays using `new`. + * Binding library functions to types via `using x for y` + * `addmod` and `mulmod` (modular addition and modular multiplication with arbitrary intermediate precision) + * Bugfix: Constructor arguments of fixed array type were not read correctly. + * Bugfix: Memory allocation of structs containing arrays or strings. + * Bugfix: Data location for explicit memory parameters in libraries was set to storage. + +### 0.1.7 (2015-11-17) + + * Improved error messages for unexpected tokens. + * Proof-of-concept transcompilation to why3 for formal verification of contracts. + * Bugfix: Arrays (also strings) as indexed parameters of events. + * Bugfix: Writing to elements of `bytes` or `string` overwrite others. + * Bugfix: "Successor block not found" on Windows. + * Bugfix: Using string literals in tuples. + * Bugfix: Cope with invalid commit hash in version for libraries. + * Bugfix: Some test framework fixes on windows. + +### 0.1.6 (2015-10-16) + + * `.push()` for dynamic storage arrays. + * Tuple expressions (`(1,2,3)` or `return (1,2,3);`) + * Declaration and assignment of multiple variables (`var (x,y,) = (1,2,3,4,5);` or `var (x,y) = f();`) + * Destructuring assignment (`(x,y,) = (1,2,3)`) + * Bugfix: Internal error about usage of library function with invalid types. + * Bugfix: Correctly parse `Library.structType a` at statement level. + * Bugfix: Correctly report source locations of parenthesized expressions (as part of "tuple" story). + +### 0.1.5 (2015-10-07) + + * Breaking change in storage encoding: Encode short byte arrays and strings together with their length in storage. + * Report warnings + * Allow storage reference types for public library functions. + * Access to types declared in other contracts and libraries via `.`. + * Version stamp at beginning of runtime bytecode of libraries. + * Bugfix: Problem with initialized string state variables and dynamic data in constructor. + * Bugfix: Resolve dependencies concerning `new` automatically. + * Bugfix: Allow four indexed arguments for anonymous events. + * Bugfix: Detect too large integer constants in functions that accept arbitrary parameters. + +### 0.1.4 (2015-09-30) + + * Bugfix: Returning fixed-size arrays. + * Bugfix: combined-json output of solc. + * Bugfix: Accessing fixed-size array return values. + * Bugfix: Disallow assignment from literal strings to storage pointers. + * Refactoring: Move type checking into its own module. + +### 0.1.3 (2015-09-25) + + * `throw` statement. + * Libraries that contain functions which are called via CALLCODE. + * Linker stage for compiler to insert other contract's addresses (used for libraries). + * Compiler option to output runtime part of contracts. + * Compile-time out of bounds check for access to fixed-size arrays by integer constants. + * Version string includes libevmasm/libethereum's version (contains the optimizer). + * Bugfix: Accessors for constant public state variables. + * Bugfix: Propagate exceptions in clone contracts. + * Bugfix: Empty single-line comments are now treated properly. + * Bugfix: Properly check the number of indexed arguments for events. + * Bugfix: Strings in struct constructors. + +### 0.1.2 (2015-08-20) + + * Improved commandline interface. + * Explicit conversion between `bytes` and `string`. + * Bugfix: Value transfer used in clone contracts. + * Bugfix: Problem with strings as mapping keys. + * Bugfix: Prevent usage of some operators. + +### 0.1.1 (2015-08-04) + + * Strings can be used as mapping keys. + * Clone contracts. + * Mapping members are skipped for structs in memory. + * Use only a single stack slot for storage references. + * Improved error message for wrong argument count. (#2456) + * Bugfix: Fix comparison between `bytesXX` types. (#2087) + * Bugfix: Do not allow floats for integer literals. (#2078) + * Bugfix: Some problem with many local variables. (#2478) + * Bugfix: Correctly initialise `string` and `bytes` state variables. + * Bugfix: Correctly compute gas requirements for callcode. + +### 0.1.0 (2015-07-10) @@ -1,23 +1,20 @@ # The Solidity Contract-Oriented Programming Language [![Join the chat at https://gitter.im/ethereum/solidity](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ethereum/solidity?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -## Useful links -To get started you can find a basic introduction to the language in the [Solidity Documentation](https://solidity.readthedocs.org). +## Useful links +To get started you can find an introduction to the language in the [Solidity documentation](https://solidity.readthedocs.org). In the documentation, you can find [code examples](http://solidity.readthedocs.io/en/latest/solidity-by-example.html) as well as [a reference](http://solidity.readthedocs.io/en/latest/solidity-in-depth.html) of the syntax and details on how to write smart contracts. You can start using [Solidity in your browser](https://ethereum.github.io/browser-solidity/) with no need to download or compile anything. -[Changelog](https://github.com/ethereum/wiki/wiki/Solidity-Changelog) - -The [Solidity Features](https://github.com/ethereum/wiki/wiki/Solidity-Features) page contains a list of already completed Solidity feature stories with explanations and demonstrations, it is being updated continuously. More technical information you can find in the [Pivotal Tracker](https://www.pivotaltracker.com/n/projects/1189488). +The changelog for this project can be found [here](https://github.com/ethereum/solidity/blob/develop/Changelog.md). -Solidity is still under development. So please do not hesitate and open an [issue in github](https://github.com/ethereum/solidity/issues) if you encounter anything strange. +Solidity is still under development. So please do not hesitate and open an [issue in GitHub](https://github.com/ethereum/solidity/issues) if you encounter anything strange. ## Building - -See the [Wiki](https://github.com/ethereum/webthree-umbrella/wiki) for build instructions, compatibility information and build tips. +See the [Solidity documentation](http://solidity.readthedocs.io/en/latest/installing-solidity.html#building-from-source) for build instructions. ## How to Contribute - This repository uses the same [coding style](https://github.com/ethereum/webthree-umbrella/blob/develop/CodingStandards.txt) as all of the cpp-ethereum projects. Please try to align with us in the gitter channel before making larger changes. + Any contributions are welcome! diff --git a/appveyor.yml b/appveyor.yml index 97f931e3..2af17912 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,12 +27,13 @@ # (c) 2016 cpp-ethereum contributors. #------------------------------------------------------------------------------ -version: 0.3.5.{build} -skip_tags: true os: Visual Studio 2015 configuration: - RelWithDebInfo -cache: build +# NB: Appveyor cache is disabled, because it is proving very unreliable. +# We can re-enable it when we find a way to mitigate the unreliability +# issues. Have automated builds be reliable is the more important thing. +#cache: build install: - git submodule update --init --recursive - scripts/install_deps.bat @@ -50,27 +51,39 @@ build_script: # - cd %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION% # - copy "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" . # - start eth.exe --test -d %TMP%\eth_for_soltest -# - soltest.exe --ipc %TMP%\eth_for_soltest\geth.ipc +# - soltest.exe -- --ipc %TMP%\eth_for_soltest\geth.ipc # - pkill eth artifacts: - - path: solidity-develop-windows.zip - name: solidity-develop-windows-zip + - path: solidity-windows.zip + name: solidity-windows-zip -# This is the deploy target for Windows -# which generates development ZIPs per commit. We are in agreement -# that this is probably noisy overkill, so will want to renable this -# functionality wrapped in some conditionals so that it only runs -# when building the 'release' branch. +# This is the deploy target for Windows which generates ZIPs per commit. +# We are in agreement that generating ZIPs per commit for the develop +# branch is probably just noise, so we only run this deployment target +# on 'release'. # -#deploy: -# release: solidity-develop-v$(APPVEYOR_BUILD_VERSION) -# tag: develop-v$(APPVEYOR_BUILD_VERSION) -# description: 'Development build of solidity at commit $(APPVEYOR_REPO_COMMIT).\n\n$(APPVEYOR_REPO_COMMIT_MESSAGE)\n\nCommitted by $(APPVEYOR_REPO_COMMIT_AUTHOR), $(APPVEYOR_REPO_COMMIT_TIMESTAMP).' -# prerelease: true -# provider: GitHub -# auth_token: -# secure: yukM9mHUbzuZSS5WSBLKSW0yGJerJEqAXkFhDhSHBBcKJE7GAryjQsdO9Kxh3yRv -# artifact: solidity-develop-windows-zip -# on: -# branch: standalone_changes +# See https://www.appveyor.com/docs/deployment/github for information +# on GitHub Releases in Appveyor. +# +# You need to generate a GitHub personal access token for Appveyor +# See https://github.com/settings/tokens for more information on that. +# The token you generate there (in an encrypted form) is what is +# passed to this deployment target in the 'auth_token' parameter +# below. + +deploy: + description: 'Development build of solidity at commit $(APPVEYOR_REPO_COMMIT).\n\n$(APPVEYOR_REPO_COMMIT_MESSAGE)\n\nCommitted by $(APPVEYOR_REPO_COMMIT_AUTHOR), $(APPVEYOR_REPO_COMMIT_TIMESTAMP).' + prerelease: true + provider: GitHub + auth_token: + secure: HPjiugbDSCsEFTphj/qwHuSw80/BV1xWoSvj95CPmtb16Ukh2VQbLVB7iFtZSans + artifact: solidity-windows-zip + on: + branch: release + +notifications: + - provider: GitHubPullRequest + on_build_success: false + on_build_failure: false + on_build_status_changed: false diff --git a/docs/common-patterns.rst b/docs/common-patterns.rst index 12c6f20c..74f9c078 100644 --- a/docs/common-patterns.rst +++ b/docs/common-patterns.rst @@ -186,13 +186,13 @@ restrictions highly readable. // refunded, but only after the function body. // This is dangerous, because if the function // uses `return` explicitly, this will not be - // done! + // done! This behavior will be fixed in Version 0.4.0. modifier costs(uint _amount) { if (msg.value < _amount) throw; _ if (msg.value > _amount) - msg.sender.send(_amount - msg.value); + msg.sender.send(msg.value - _amount); } function forceOwnerChange(address _newOwner) @@ -267,7 +267,9 @@ function finishes. the code in the transitionNext modifier can be skipped if the function itself uses return. If you want to do that, make sure - to call nextStage manually from those functions. + to call nextStage manually from those functions. + With version 0.4.0 (unreleased), modifier code + will run even if the function explicitly returns. :: diff --git a/docs/contracts.rst b/docs/contracts.rst index 3d592ecf..5f370951 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -314,14 +314,40 @@ inheritable properties of contracts and may be overridden by derived contracts. } } + contract Mutex { + bool locked; + modifier noReentrancy() { + if (locked) throw; + locked = true; + _ + locked = false; + } + + /// This function is protected by a mutex, which means that + /// reentrant calls from within msg.sender.call cannot call f again. + /// The `return 7` statement assigns 7 to the return value but still + /// executes the statement `locked = false` in the modifier. + function f() noReentrancy returns (uint) { + if (!msg.sender.call()) throw; + return 7; + } + } + Multiple modifiers can be applied to a function by specifying them in a -whitespace-separated list and will be evaluated in order. Explicit returns from -a modifier or function body immediately leave the whole function, while control -flow reaching the end of a function or modifier body continues after the "_" in -the preceding modifier. Arbitrary expressions are allowed for modifier -arguments and in this context, all symbols visible from the function are -visible in the modifier. Symbols introduced in the modifier are not visible in -the function (as they might change by overriding). +whitespace-separated list and will be evaluated in order. + +.. warning:: + In an earlier version of Solidity, ``return`` statements in functions + having modifiers behaved differently. + +Explicit returns from a modifier or function body only leave the current +modifier or function body. Return variables are assigned and +control flow continues after the "_" in the preceding modifier. + +Arbitrary expressions are allowed for modifier arguments and in this context, +all symbols visible from the function are visible in the modifier. Symbols +introduced in the modifier are not visible in the function (as they might +change by overriding). .. index:: ! constant diff --git a/docs/control-structures.rst b/docs/control-structures.rst index 9d7ebeac..73b0131f 100644 --- a/docs/control-structures.rst +++ b/docs/control-structures.rst @@ -10,7 +10,7 @@ Control Structures Most of the control structures from C/JavaScript are available in Solidity except for ``switch`` and ``goto``. So there is: ``if``, ``else``, ``while``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with -the usual semantics known from C / JavaScript. +the usual semantics known from C or JavaScript. Parentheses can *not* be omitted for conditionals, but curly brances can be omitted around single-statement bodies. @@ -69,6 +69,10 @@ this does not execute a constructor. We could also have used ``function setFeed( only (locally) sets the value and amount of gas sent with the function call and only the parentheses at the end perform the actual call. +Function calls cause exceptions if the called contract does not exist (in the +sense that the account does not contain code) or if the called contract itself +throws an exception or goes out of gas. + .. warning:: Any interaction with another contract imposes a potential danger, especially if the source code of the contract is not known in advance. The current @@ -82,13 +86,15 @@ parentheses at the end perform the actual call. that the called contract can change state variables of the calling contract via its functions. Write your functions in a way that, for example, calls to external functions happen after any changes to state variables in your contract - so your contract is not vulnerable to a recursive call exploit. + so your contract is not vulnerable to a reentrancy exploit. Named Calls and Anonymous Function Parameters --------------------------------------------- -Function call arguments can also be given by name, in any order, and the names -of unused parameters (especially return parameters) can be omitted. +Function call arguments can also be given by name, in any order, +if they are enclosed in ``{ }`` as can be seen in the following +example. The argument list has to coincide by name with the list of +parameters from the function declaration, but can be in arbitrary order. :: @@ -99,12 +105,60 @@ of unused parameters (especially return parameters) can be omitted. // named arguments f({value: 2, key: 3}); } + } + +Omitted Function Parameter Names +-------------------------------- + +The names of unused parameters (especially return parameters) can be omitted. +Those names will still be present on the stack, but they are inaccessible. + +:: - // omitted parameters + contract C { + // omitted name for parameter function func(uint k, uint) returns(uint) { return k; } } + + +.. index:: ! new, contracts;creating + +.. _creating-contracts: + +Creating Contracts via new +========================== + +A contract can create a new contract using the ``new`` keyword. The full +code of the contract to be created has to be known and thus recursive +creation-dependencies are now possible. + +:: + + contract D { + uint x; + function D(uint a) { + x = a; + } + } + contract C { + D d = new D(4); // will be executed as part of C's constructor + + function createD(uint arg) { + D newD = new D(arg); + } + + function createAndEndowD(uint arg, uint amount) { + // Send ether along with the creation + D newD = (new D).value(amount)(arg); + } + } + +As seen in the example, it is possible to forward Ether to the creation, +but it is not possible to limit the amount of gas. If the creation fails +(due to out-of-stack, not enough balance or other problems), an exception +is thrown. Order of Evaluation of Expressions ================================== @@ -245,11 +299,14 @@ In the following example, we show how ``throw`` can be used to easily revert an } } -Currently, there are three situations, where exceptions happen automatically in Solidity: +Currently, there are six situations, where exceptions happen automatically in Solidity: -1. If you access an array beyond its length (i.e. ``x[i]`` where ``i >= x.length``) +1. If you access an array beyond its length (i.e. ``x[i]`` where ``i >= x.length``). 2. If a function called via a message call does not finish properly (i.e. it runs out of gas or throws an exception itself). 3. If a non-existent function on a library is called or Ether is sent to a library. +4. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``). +5. If you perform an external function call targeting a contract that contains no code. +6. If a contract-creation call using the ``new`` keyword fails. Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect. @@ -337,9 +394,9 @@ Inline assembly parses comments, literals and identifiers exactly as Solidity, s usual ``//`` and ``/* */`` comments. Inline assembly is initiated by ``assembly { ... }`` and inside these curly braces, the following can be used (see the later sections for more details) - - literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) + - literals, e.g. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters) - opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below - - opcode in functional style, e.g. ``add(1, mlod(0))`` + - opcodes in functional style, e.g. ``add(1, mlod(0))`` - labels, e.g. ``name:`` - variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)`` - identifiers (externals, labels or assembly-local variables), e.g. ``jump(name)``, ``3 x add`` @@ -354,7 +411,7 @@ This document does not want to be a full description of the Ethereum virtual mac following list can be used as a reference of its opcodes. If an opcode takes arguments (always from the top of the stack), they are given in parentheses. -Note that the order of arguments can be seed to be reversed in non-functional style (explained below). +Note that the order of arguments can be seen as being reversed compared to the instructional 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 onte the stack. diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst index b3667a11..c28b4ab7 100644 --- a/docs/frequently-asked-questions.rst +++ b/docs/frequently-asked-questions.rst @@ -399,7 +399,7 @@ What character set does Solidity use? ===================================== Solidity is character set agnostic concerning strings in the source code, although -utf-8 is recommended. Identifiers (variables, functions, ...) can only use +UTF-8 is recommended. Identifiers (variables, functions, ...) can only use ASCII. What are some examples of basic string manipulation (``substring``, ``indexOf``, ``charAt``, etc)? @@ -741,15 +741,15 @@ see a 32-byte hex value, this is just ``"stringliteral"`` in hex. The type ``bytes`` is similar, only that it can change its length. Finally, ``string`` is basically identical to ``bytes`` only that it is assumed -to hold the utf-8 encoding of a real string. Since ``string`` stores the -data in utf-8 encoding it is quite expensive to compute the number of +to hold the UTF-8 encoding of a real string. Since ``string`` stores the +data in UTF-8 encoding it is quite expensive to compute the number of characters in the string (the encoding of some characters takes more than a single byte). Because of that, ``string s; s.length`` is not yet supported and not even index access ``s[2]``. But if you want to access the low-level byte encoding of the string, you can use ``bytes(s).length`` and ``bytes(s)[2]`` which will result in the number -of bytes in the utf-8 encoding of the string (not the number of -characters) and the second byte (not character) of the utf-8 encoded +of bytes in the UTF-8 encoding of the string (not the number of +characters) and the second byte (not character) of the UTF-8 encoded string, respectively. diff --git a/docs/index.rst b/docs/index.rst index 555ffaa7..a330172e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -56,6 +56,9 @@ Available Solidity Integrations * `Vim Solidity <https://github.com/tomlion/vim-solidity/>`_ Plugin for the Vim editor providing syntax highlighting. +* `Vim Syntastic <https://github.com/scrooloose/syntastic>`_ + Plugin for the Vim editor providing compile checking. + Discontinued: * `Mix IDE <https://github.com/ethereum/mix/>`_ diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst index a98a1ade..16a02310 100644 --- a/docs/installing-solidity.rst +++ b/docs/installing-solidity.rst @@ -61,7 +61,7 @@ Set up Homebrew: brew upgrade brew install boost --c++11 # this takes a while - brew install cmake cryptopp gmp + brew install cmake cryptopp gmp jsoncpp Ubuntu Trusty (14.04) --------------------- diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 304fce14..804d69ef 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -228,7 +228,7 @@ The following is the order of precedence for operators, listed in order of evalu + +-------------------------------------+--------------------------------------------+ | | Unary plus and minus | ``+``, ``-`` | + +-------------------------------------+--------------------------------------------+ -| | Unary operations | ``after``, ``delete`` | +| | Unary operations | ``delete`` | + +-------------------------------------+--------------------------------------------+ | | Logical NOT | ``!`` | + +-------------------------------------+--------------------------------------------+ @@ -286,7 +286,7 @@ Global Variables - ``sha3(...) returns (bytes32)``: compute the Ethereum-SHA-3 (KECCAK-256) hash of the (tightly packed) arguments - ``sha256(...) returns (bytes32)``: compute the SHA-256 hash of the (tightly packed) arguments - ``ripemd160(...) returns (bytes20)``: compute the RIPEMD-160 hash of the (tightly packed) arguments -- ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: recover address associated with the public key from elliptic curve signature +- ``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`` - ``this`` (current contract's type): the current contract, explicitly convertible to ``address`` @@ -321,4 +321,3 @@ Modifiers - ``constant`` for functions: Disallows modification of state - this is not enforced yet. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. - diff --git a/docs/style-guide.rst b/docs/style-guide.rst index c509a9d4..c7b13efa 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -118,7 +118,7 @@ Source File Encoding UTF-8 or ASCII encoding is preferred. Imports -========== +======= Import statements should always be placed at the top of the file. @@ -519,6 +519,18 @@ No:: Other Recommendations ===================== +* Strings should be quoted with double-quotes instead of single-quotes. + +Yes:: + + str = "foo"; + str = "Hamlet says, 'To be or not to be...'"; + +No:: + + str = 'bar'; + str = '"Be yourself; everyone else is already taken." -Oscar Wilde'; + * Surround operators with a single space on either side. Yes:: diff --git a/docs/types.rst b/docs/types.rst index 0c5aaf1b..d6445ed9 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -57,6 +57,8 @@ Operators: Division always truncates (it just maps to the DIV opcode of the EVM), but it does not truncate if both operators are :ref:`literals<rational_literals>` (or literal expressions). +Division by zero and modulus with zero throws an exception. + .. index:: address, balance, send, call, callcode, delegatecall .. _address: @@ -147,10 +149,10 @@ Dynamically-sized byte array ``bytes``: Dynamically-sized byte array, see :ref:`arrays`. Not a value-type! ``string``: - Dynamically-sized UTF8-encoded string, see :ref:`arrays`. Not a value-type! + Dynamically-sized UTF-8-encoded string, see :ref:`arrays`. Not a value-type! As a rule of thumb, use ``bytes`` for arbitrary-length raw byte data and ``string`` -for arbitrary-length string (utf-8) data. If you can limit the length to a certain +for arbitrary-length string (UTF-8) data. If you can limit the length to a certain number of bytes, always use one of ``bytes1`` to ``bytes32`` because they are much cheaper. .. index:: ! ufixed, ! fixed, ! fixed point number @@ -214,9 +216,18 @@ a non-rational number). String Literals --------------- -String Literals are written with double quotes (``"abc"``). As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32`` if they fit, to ``bytes`` and to ``string``. +String literals are written with either double or single-quotes (``"foo"`` or ``'bar'``). As with integer literals, their type can vary, but they are implicitly convertible to ``bytes1``, ..., ``bytes32``, if they fit, to ``bytes`` and to ``string``. + +String literals support escape characters, such as ``\n``, ``\xNN`` and ``\uNNNN``. ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF-8 sequence. + +.. index:: literal, bytes + +Hexadecimal Literals +-------------------- + +Hexademical Literals are prefixed with the keyword ``hex`` and are enclosed in double or single-quotes (``hex"001122FF"``). Their content must be a hexadecimal string and their value will be the binary representation of those values. -String Literals support escape characters, such as ``\n``, ``\xNN`` and ``\uNNNN``. ``\xNN`` takes a hex value and inserts the appropriate byte, while ``\uNNNN`` takes a Unicode codepoint and inserts an UTF8 sequence. +Hexademical Literals behave like String Literals and have the same convertibility restrictions. .. index:: enum @@ -353,7 +364,7 @@ So ``bytes`` should always be preferred over ``byte[]`` because it is cheaper. .. note:: If you want to access the byte-representation of a string ``s``, use ``bytes(s).length`` / ``bytes(s)[7] = 'x';``. Keep in mind - that you are accessing the low-level bytes of the utf-8 representation, + that you are accessing the low-level bytes of the UTF-8 representation, and not the individual characters! .. index:: ! array;allocating, new diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst index 8b8f4daa..d1d578ed 100644 --- a/docs/units-and-global-variables.rst +++ b/docs/units-and-global-variables.rst @@ -7,14 +7,14 @@ Units and Globally Available Variables Ether Units =========== -A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be "wei", e.g. ``2 ether == 2000 finney`` evaluates to ``true``. +A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to convert between the subdenominations of Ether, where Ether currency numbers without a postfix are assumed to be Wei, e.g. ``2 ether == 2000 finney`` evaluates to ``true``. .. index:: time, seconds, minutes, hours, days, weeks, years Time Units ========== -Suffixes of ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks`` and +Suffixes like ``seconds``, ``minutes``, ``hours``, ``days``, ``weeks`` and ``years`` after literal numbers can be used to convert between units of time where seconds are the base unit and units are considered naively in the following way: @@ -95,7 +95,7 @@ Mathematical and Cryptographic Functions ``ripemd160(...) returns (bytes20)``: compute RIPEMD-160 hash of the (tightly packed) arguments ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: - recover the address associated with the public key from elliptic curve signature + recover the address associated with the public key from elliptic curve signature or return zero on error In the above, "tightly packed" means that the arguments are concatenated without padding. This means that the following are all identical:: diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index e94ce9fe..593f2f69 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -40,13 +40,26 @@ void SyntaxChecker::syntaxError(SourceLocation const& _location, std::string con m_errors.push_back(err); } +bool SyntaxChecker::visit(ModifierDefinition const&) +{ + m_placeholderFound = false; + return true; +} + +void SyntaxChecker::endVisit(ModifierDefinition const& _modifier) +{ + if (!m_placeholderFound) + syntaxError(_modifier.body().location(), "Modifier body does not contain '_'."); + m_placeholderFound = false; +} + bool SyntaxChecker::visit(WhileStatement const&) { m_inLoopDepth++; return true; } -void SyntaxChecker::endVisit(WhileStatement const&) +void SyntaxChecker::endVisit(WhileStatement const& ) { m_inLoopDepth--; } @@ -78,3 +91,9 @@ bool SyntaxChecker::visit(Break const& _breakStatement) return true; } +bool SyntaxChecker::visit(const PlaceholderStatement&) +{ + m_placeholderFound = true; + return true; +} + diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index c836d49f..3198ffd0 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -31,6 +31,7 @@ namespace solidity /** * The module that performs syntax analysis on the AST: * - whether continue/break is in a for/while loop. + * - whether a modifier contains at least one '_' */ class SyntaxChecker: private ASTConstVisitor { @@ -44,6 +45,9 @@ private: /// Adds a new error to the list of errors. void syntaxError(SourceLocation const& _location, std::string const& _description); + virtual bool visit(ModifierDefinition const& _modifier) override; + virtual void endVisit(ModifierDefinition const& _modifier) override; + virtual bool visit(WhileStatement const& _whileStatement) override; virtual void endVisit(WhileStatement const& _whileStatement) override; virtual bool visit(ForStatement const& _forStatement) override; @@ -52,8 +56,13 @@ private: virtual bool visit(Continue const& _continueStatement) override; virtual bool visit(Break const& _breakStatement) override; + virtual bool visit(PlaceholderStatement const& _placeholderStatement) override; + ErrorList& m_errors; + /// Flag that indicates whether a function modifier actually contains '_'. + bool m_placeholderFound = false; + int m_inLoopDepth = 0; }; diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 6b459da4..d23c4890 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -86,14 +86,9 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location) ASTJsonConverter::ASTJsonConverter( ASTNode const& _ast, - map<string, unsigned> const& _sourceIndices + map<string, unsigned> _sourceIndices ): m_ast(&_ast), m_sourceIndices(_sourceIndices) { - Json::Value children(Json::arrayValue); - - m_astJson["name"] = "root"; - m_astJson["children"] = children; - m_jsonNodePtrs.push(&m_astJson["children"]); } void ASTJsonConverter::print(ostream& _stream) @@ -108,21 +103,56 @@ Json::Value const& ASTJsonConverter::json() return m_astJson; } +bool ASTJsonConverter::visit(SourceUnit const&) +{ + Json::Value children(Json::arrayValue); + + m_astJson["name"] = "SourceUnit"; + m_astJson["children"] = children; + m_jsonNodePtrs.push(&m_astJson["children"]); + + return true; +} + bool ASTJsonConverter::visit(ImportDirective const& _node) { - addJsonNode(_node, "Import", { make_pair("file", _node.path())}); + addJsonNode(_node, "ImportDirective", { make_pair("file", _node.path())}); return true; } bool ASTJsonConverter::visit(ContractDefinition const& _node) { - addJsonNode(_node, "Contract", { make_pair("name", _node.name()) }, true); + addJsonNode(_node, "ContractDefinition", { make_pair("name", _node.name()) }, true); + return true; +} + +bool ASTJsonConverter::visit(InheritanceSpecifier const& _node) +{ + addJsonNode(_node, "InheritanceSpecifier", {}, true); + return true; +} + +bool ASTJsonConverter::visit(UsingForDirective const& _node) +{ + addJsonNode(_node, "UsingForDirective", {}, true); return true; } bool ASTJsonConverter::visit(StructDefinition const& _node) { - addJsonNode(_node, "Struct", { make_pair("name", _node.name()) }, true); + addJsonNode(_node, "StructDefinition", { make_pair("name", _node.name()) }, true); + return true; +} + +bool ASTJsonConverter::visit(EnumDefinition const& _node) +{ + addJsonNode(_node, "EnumDefinition", { make_pair("name", _node.name()) }, true); + return true; +} + +bool ASTJsonConverter::visit(EnumValue const& _node) +{ + addJsonNode(_node, "EnumValue", { make_pair("name", _node.name()) }); return true; } @@ -134,7 +164,7 @@ bool ASTJsonConverter::visit(ParameterList const& _node) bool ASTJsonConverter::visit(FunctionDefinition const& _node) { - addJsonNode(_node, "Function", + addJsonNode(_node, "FunctionDefinition", { make_pair("name", _node.name()), make_pair("public", boost::lexical_cast<std::string>(_node.isPublic())), make_pair("const", boost::lexical_cast<std::string>(_node.isDeclaredConst())) }, @@ -151,11 +181,29 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node) return true; } +bool ASTJsonConverter::visit(ModifierDefinition const& _node) +{ + addJsonNode(_node, "ModifierDefinition", { make_pair("name", _node.name()) }, true); + return true; +} + +bool ASTJsonConverter::visit(ModifierInvocation const& _node) +{ + addJsonNode(_node, "ModifierInvocation", {}, true); + return true; +} + bool ASTJsonConverter::visit(TypeName const&) { return true; } +bool ASTJsonConverter::visit(EventDefinition const& _node) +{ + addJsonNode(_node, "EventDefinition", { make_pair("name", _node.name()) }, true); + return true; +} + bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { addJsonNode(_node, "ElementaryTypeName", { make_pair("name", _node.typeName().toString()) }); @@ -176,6 +224,12 @@ bool ASTJsonConverter::visit(Mapping const& _node) return true; } +bool ASTJsonConverter::visit(ArrayTypeName const& _node) +{ + addJsonNode(_node, "ArrayTypeName", {}, true); + return true; +} + bool ASTJsonConverter::visit(InlineAssembly const& _node) { addJsonNode(_node, "InlineAssembly", {}, true); @@ -188,6 +242,12 @@ bool ASTJsonConverter::visit(Block const& _node) return true; } +bool ASTJsonConverter::visit(PlaceholderStatement const& _node) +{ + addJsonNode(_node, "PlaceholderStatement", {}); + return true; +} + bool ASTJsonConverter::visit(IfStatement const& _node) { addJsonNode(_node, "IfStatement", {}, true); @@ -232,7 +292,7 @@ bool ASTJsonConverter::visit(Throw const& _node) bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node) { - addJsonNode(_node, "VariableDefinition", {}, true); + addJsonNode(_node, "VariableDefinitionStatement", {}, true); return true; } @@ -336,6 +396,11 @@ bool ASTJsonConverter::visit(Literal const& _node) return true; } +void ASTJsonConverter::endVisit(SourceUnit const&) +{ + goUp(); +} + void ASTJsonConverter::endVisit(ImportDirective const&) { } @@ -345,11 +410,30 @@ void ASTJsonConverter::endVisit(ContractDefinition const&) goUp(); } +void ASTJsonConverter::endVisit(InheritanceSpecifier const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(UsingForDirective const&) +{ + goUp(); +} + void ASTJsonConverter::endVisit(StructDefinition const&) { goUp(); } +void ASTJsonConverter::endVisit(EnumDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(EnumValue const&) +{ +} + void ASTJsonConverter::endVisit(ParameterList const&) { goUp(); @@ -362,6 +446,22 @@ void ASTJsonConverter::endVisit(FunctionDefinition const&) void ASTJsonConverter::endVisit(VariableDeclaration const&) { + goUp(); +} + +void ASTJsonConverter::endVisit(ModifierDefinition const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(ModifierInvocation const&) +{ + goUp(); +} + +void ASTJsonConverter::endVisit(EventDefinition const&) +{ + goUp(); } void ASTJsonConverter::endVisit(TypeName const&) @@ -378,10 +478,17 @@ void ASTJsonConverter::endVisit(UserDefinedTypeName const&) void ASTJsonConverter::endVisit(Mapping const&) { + goUp(); +} + +void ASTJsonConverter::endVisit(ArrayTypeName const&) +{ + goUp(); } void ASTJsonConverter::endVisit(InlineAssembly const&) { + goUp(); } void ASTJsonConverter::endVisit(Block const&) @@ -389,6 +496,10 @@ void ASTJsonConverter::endVisit(Block const&) goUp(); } +void ASTJsonConverter::endVisit(PlaceholderStatement const&) +{ +} + void ASTJsonConverter::endVisit(IfStatement const&) { goUp(); diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h index 2e3046f1..192c4c4f 100644 --- a/libsolidity/ast/ASTJsonConverter.h +++ b/libsolidity/ast/ASTJsonConverter.h @@ -45,24 +45,34 @@ public: /// @a _sourceIndices is used to abbreviate source names in source locations. explicit ASTJsonConverter( ASTNode const& _ast, - std::map<std::string, unsigned> const& _sourceIndices = std::map<std::string, unsigned>() + std::map<std::string, unsigned> _sourceIndices = std::map<std::string, unsigned>() ); /// Output the json representation of the AST to _stream. void print(std::ostream& _stream); Json::Value const& json(); + bool visit(SourceUnit const& _node) override; bool visit(ImportDirective const& _node) override; bool visit(ContractDefinition const& _node) override; + bool visit(InheritanceSpecifier const& _node) override; + bool visit(UsingForDirective const& _node) override; bool visit(StructDefinition const& _node) override; + bool visit(EnumDefinition const& _node) override; + bool visit(EnumValue const& _node) override; bool visit(ParameterList const& _node) override; bool visit(FunctionDefinition const& _node) override; bool visit(VariableDeclaration const& _node) override; + bool visit(ModifierDefinition const& _node) override; + bool visit(ModifierInvocation const& _node) override; + bool visit(EventDefinition const& _node) override; bool visit(TypeName const& _node) override; bool visit(ElementaryTypeName const& _node) override; bool visit(UserDefinedTypeName const& _node) override; bool visit(Mapping const& _node) override; + bool visit(ArrayTypeName const& _node) override; bool visit(InlineAssembly const& _node) override; bool visit(Block const& _node) override; + bool visit(PlaceholderStatement const& _node) override; bool visit(IfStatement const& _node) override; bool visit(WhileStatement const& _node) override; bool visit(ForStatement const& _node) override; @@ -85,18 +95,28 @@ public: bool visit(ElementaryTypeNameExpression const& _node) override; bool visit(Literal const& _node) override; + void endVisit(SourceUnit const&) override; void endVisit(ImportDirective const&) override; void endVisit(ContractDefinition const&) override; + void endVisit(InheritanceSpecifier const&) override; + void endVisit(UsingForDirective const&) override; void endVisit(StructDefinition const&) override; + void endVisit(EnumDefinition const&) override; + void endVisit(EnumValue const&) override; void endVisit(ParameterList const&) override; void endVisit(FunctionDefinition const&) override; void endVisit(VariableDeclaration const&) override; + void endVisit(ModifierDefinition const&) override; + void endVisit(ModifierInvocation const&) override; + void endVisit(EventDefinition const&) override; void endVisit(TypeName const&) override; void endVisit(ElementaryTypeName const&) override; void endVisit(UserDefinedTypeName const&) override; void endVisit(Mapping const&) override; + void endVisit(ArrayTypeName const&) override; void endVisit(InlineAssembly const&) override; void endVisit(Block const&) override; + void endVisit(PlaceholderStatement const&) override; void endVisit(IfStatement const&) override; void endVisit(WhileStatement const&) override; void endVisit(ForStatement const&) override; @@ -141,7 +161,7 @@ private: Json::Value m_astJson; std::stack<Json::Value*> m_jsonNodePtrs; ASTNode const* m_ast; - std::map<std::string, unsigned> const& m_sourceIndices; + std::map<std::string, unsigned> m_sourceIndices; }; } diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 28f7e1b7..d86a2caf 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -306,7 +306,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // for non-address integers, we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || - _operator == Token::After || _operator == Token::BitNot) + _operator == Token::BitNot) return shared_from_this(); else return TypePointer(); @@ -416,8 +416,7 @@ TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const _operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || - _operator == Token::Dec || - _operator == Token::After + _operator == Token::Dec ) return shared_from_this(); else diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index c1dc8dfb..3ac5bd3c 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -23,9 +23,12 @@ #include <libsolidity/codegen/CompilerContext.h> #include <utility> #include <numeric> +#include <boost/algorithm/string/replace.hpp> #include <libsolidity/ast/AST.h> #include <libsolidity/codegen/Compiler.h> #include <libsolidity/interface/Version.h> +#include <libsolidity/inlineasm/AsmData.h> +#include <libsolidity/inlineasm/AsmStack.h> using namespace std; @@ -172,6 +175,51 @@ void CompilerContext::resetVisitedNodes(ASTNode const* _node) updateSourceLocation(); } +void CompilerContext::appendInlineAssembly( + string const& _assembly, + vector<string> const& _localVariables, + map<string, string> const& _replacements +) +{ + string replacedAssembly; + string const* assembly = &_assembly; + if (!_replacements.empty()) + { + replacedAssembly = _assembly; + for (auto const& replacement: _replacements) + replacedAssembly = boost::algorithm::replace_all_copy(replacedAssembly, replacement.first, replacement.second); + assembly = &replacedAssembly; + } + + unsigned startStackHeight = stackHeight(); + auto identifierAccess = [&]( + assembly::Identifier const& _identifier, + eth::Assembly& _assembly, + assembly::CodeGenerator::IdentifierContext _context + ) { + auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name); + if (it == _localVariables.end()) + return false; + unsigned stackDepth = _localVariables.end() - it; + int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; + if (stackDiff < 1 || stackDiff > 16) + BOOST_THROW_EXCEPTION( + CompilerError() << + errinfo_comment("Stack too deep, try removing local variables.") + ); + if (_context == assembly::CodeGenerator::IdentifierContext::RValue) + _assembly.append(dupInstruction(stackDiff)); + else + { + _assembly.append(swapInstruction(stackDiff)); + _assembly.append(Instruction::POP); + } + return true; + }; + + solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, m_asm, identifierAccess), ""); +} + void CompilerContext::injectVersionStampIntoSub(size_t _subIndex) { eth::Assembly& sub = m_asm.sub(_subIndex); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index a56335ce..0c1500b0 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -132,6 +132,15 @@ public: CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; } CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; } + /// Appends inline assembly. @a _replacements are string-matching replacements that are performed + /// prior to parsing the inline assembly. + /// @param _localVariables assigns stack positions to variables with the last one being the stack top + void appendInlineAssembly( + std::string const& _assembly, + std::vector<std::string> const& _localVariables = std::vector<std::string>(), + std::map<std::string, std::string> const& _replacements = std::map<std::string, std::string>{} + ); + /// Prepends "PUSH <compiler version number> POP" void injectVersionStampIntoSub(size_t _subIndex); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index bcfd33f2..715852be 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -431,16 +431,16 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope()))) appendBaseConstructor(*c); - m_returnTag = m_context.newTag(); + solAssert(m_returnTags.empty(), ""); m_breakTags.clear(); m_continueTags.clear(); m_stackCleanupForReturn = 0; m_currentFunction = &_function; - m_modifierDepth = 0; + m_modifierDepth = -1; appendModifierOrFunctionCode(); - m_context << m_returnTag; + solAssert(m_returnTags.empty(), ""); // Now we need to re-shuffle the stack. For this we keep a record of the stack layout // that shows the target positions of the elements, where "-1" denotes that this element needs @@ -695,7 +695,7 @@ bool ContractCompiler::visit(Return const& _return) } for (unsigned i = 0; i < m_stackCleanupForReturn; ++i) m_context << Instruction::POP; - m_context.appendJumpTo(m_returnTag); + m_context.appendJumpTo(m_returnTags.back()); m_context.adjustStackOffset(m_stackCleanupForReturn); return false; } @@ -755,9 +755,7 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement) { StackHeightChecker checker(m_context); CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement); - ++m_modifierDepth; appendModifierOrFunctionCode(); - --m_modifierDepth; checker.check(); return true; } @@ -775,10 +773,15 @@ void ContractCompiler::appendMissingFunctions() void ContractCompiler::appendModifierOrFunctionCode() { solAssert(m_currentFunction, ""); + unsigned stackSurplus = 0; + Block const* codeBlock = nullptr; + + m_modifierDepth++; + if (m_modifierDepth >= m_currentFunction->modifiers().size()) { solAssert(m_currentFunction->isImplemented(), ""); - m_currentFunction->body().accept(*this); + codeBlock = &m_currentFunction->body(); } else { @@ -786,37 +789,45 @@ void ContractCompiler::appendModifierOrFunctionCode() // constructor call should be excluded if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration)) - { - ++m_modifierDepth; appendModifierOrFunctionCode(); - --m_modifierDepth; - return; - } - - ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name()); - CompilerContext::LocationSetter locationSetter(m_context, modifier); - solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); - for (unsigned i = 0; i < modifier.parameters().size(); ++i) + else { - m_context.addVariable(*modifier.parameters()[i]); - compileExpression( - *modifierInvocation->arguments()[i], - modifier.parameters()[i]->annotation().type - ); + ModifierDefinition const& modifier = m_context.functionModifier(modifierInvocation->name()->name()); + CompilerContext::LocationSetter locationSetter(m_context, modifier); + solAssert(modifier.parameters().size() == modifierInvocation->arguments().size(), ""); + for (unsigned i = 0; i < modifier.parameters().size(); ++i) + { + m_context.addVariable(*modifier.parameters()[i]); + compileExpression( + *modifierInvocation->arguments()[i], + modifier.parameters()[i]->annotation().type + ); + } + for (VariableDeclaration const* localVariable: modifier.localVariables()) + appendStackVariableInitialisation(*localVariable); + + stackSurplus = + CompilerUtils::sizeOnStack(modifier.parameters()) + + CompilerUtils::sizeOnStack(modifier.localVariables()); + codeBlock = &modifier.body(); + + codeBlock = &modifier.body(); } - for (VariableDeclaration const* localVariable: modifier.localVariables()) - appendStackVariableInitialisation(*localVariable); + } + + if (codeBlock) + { + m_returnTags.push_back(m_context.newTag()); - unsigned const c_stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters()) + - CompilerUtils::sizeOnStack(modifier.localVariables()); - m_stackCleanupForReturn += c_stackSurplus; + codeBlock->accept(*this); - modifier.body().accept(*this); + solAssert(!m_returnTags.empty(), ""); + m_context << m_returnTags.back(); + m_returnTags.pop_back(); - for (unsigned i = 0; i < c_stackSurplus; ++i) - m_context << Instruction::POP; - m_stackCleanupForReturn -= c_stackSurplus; + CompilerUtils(m_context).popStackSlots(stackSurplus); } + m_modifierDepth--; } void ContractCompiler::appendStackVariableInitialisation(VariableDeclaration const& _variable) diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index d1517e88..0799a543 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -40,11 +40,9 @@ class ContractCompiler: private ASTConstVisitor public: explicit ContractCompiler(CompilerContext& _context, bool _optimise): m_optimise(_optimise), - m_context(_context), - m_returnTag(eth::Tag, u256(-1)) + m_context(_context) { m_context = CompilerContext(); - m_returnTag = m_context.newTag(); } void compileContract( @@ -122,7 +120,8 @@ private: CompilerContext& m_context; std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement - eth::AssemblyItem m_returnTag; ///< tag to jump to for a "return" statement + /// Tag to jump to for a "return" statement, needs to be stacked because of modifiers. + std::vector<eth::AssemblyItem> m_returnTags; unsigned m_modifierDepth = 0; FunctionDefinition const* m_currentFunction = nullptr; unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 80009a90..4a81e27d 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -23,6 +23,7 @@ #include <utility> #include <numeric> #include <boost/range/adaptor/reversed.hpp> +#include <boost/algorithm/string/replace.hpp> #include <libdevcore/Common.h> #include <libdevcore/SHA3.h> #include <libsolidity/ast/AST.h> @@ -297,9 +298,6 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) case Token::BitNot: // ~ m_context << Instruction::NOT; break; - case Token::After: // after - m_context << Instruction::TIMESTAMP << Instruction::ADD; - break; case Token::Delete: // delete solAssert(!!m_currentLValue, "LValue not retrieved."); m_currentLValue->setToZero(_unaryOperation.location()); @@ -536,6 +534,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) else m_context << u256(0); m_context << Instruction::CREATE; + // Check if zero (out of stack or not enough balance). + m_context << Instruction::DUP1 << Instruction::ISZERO; + m_context.appendConditionalJumpTo(m_context.errorTag()); if (function.valueSet()) m_context << swapInstruction(1) << Instruction::POP; break; @@ -1323,11 +1324,18 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty m_context << Instruction::MUL; break; case Token::Div: - m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); - break; case Token::Mod: - m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD); + { + // Test for division by zero + m_context << Instruction::DUP2 << Instruction::ISZERO; + m_context.appendConditionalJumpTo(m_context.errorTag()); + + if (_operator == Token::Div) + m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); + else + m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD); break; + } case Token::Exp: m_context << Instruction::EXP; break; @@ -1448,6 +1456,19 @@ void ExpressionCompiler::appendExternalFunctionCall( argumentTypes.push_back(_arguments[i]->annotation().type); } + if (funKind == FunctionKind::ECRecover) + { + // Clears 32 bytes of currently free memory and advances free memory pointer. + // Output area will be "start of input area" - 32. + // The reason is that a failing ECRecover cannot be detected, it will just return + // zero bytes (which we cannot detect). + solAssert(0 < retSize && retSize <= 32, ""); + utils().fetchFreeMemoryPointer(); + m_context << Instruction::DUP1 << u256(0) << Instruction::MSTORE; + m_context << u256(32) << Instruction::ADD; + utils().storeFreeMemoryPointer(); + } + // Copy function identifier to memory. utils().fetchFreeMemoryPointer(); if (!_functionType.isBareCall() || manualFunctionId) @@ -1456,7 +1477,7 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false); } // If the function takes arbitrary parameters, copy dynamic length data in place. - // Move argumenst to memory, will not update the free memory pointer (but will update the memory + // Move arguments to memory, will not update the free memory pointer (but will update the memory // pointer on the stack). utils().encodeToMemory( argumentTypes, @@ -1474,12 +1495,24 @@ void ExpressionCompiler::appendExternalFunctionCall( // function identifier [unless bare] // contract address - // Output data will replace input data. + // Output data will replace input data, unless we have ECRecover (then, output + // area will be 32 bytes just before input area). // put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input> m_context << u256(retSize); - utils().fetchFreeMemoryPointer(); - m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB; - m_context << Instruction::DUP2; + utils().fetchFreeMemoryPointer(); // This is the start of input + if (funKind == FunctionKind::ECRecover) + { + // In this case, output is 32 bytes before input and has already been cleared. + m_context << u256(32) << Instruction::DUP2 << Instruction::SUB << Instruction::SWAP1; + // Here: <input end> <output size> <outpos> <input pos> + m_context << Instruction::DUP1 << Instruction::DUP5 << Instruction::SUB; + m_context << Instruction::SWAP1; + } + else + { + m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB; + m_context << Instruction::DUP2; + } // CALL arguments: outSize, outOff, inSize, inOff (already present up to here) // [value,] addr, gas (stack top) @@ -1491,6 +1524,13 @@ void ExpressionCompiler::appendExternalFunctionCall( m_context << u256(0); m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); + // Check the the target contract exists (has code) for non-low-level calls. + if (funKind == FunctionKind::External || funKind == FunctionKind::CallCode || funKind == FunctionKind::DelegateCall) + { + m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; + m_context.appendConditionalJumpTo(m_context.errorTag()); + } + if (_functionType.gasSet()) m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); else @@ -1542,6 +1582,14 @@ void ExpressionCompiler::appendExternalFunctionCall( utils().loadFromMemoryDynamic(IntegerType(160), false, true, false); utils().convertType(IntegerType(160), FixedBytesType(20)); } + else if (funKind == FunctionKind::ECRecover) + { + // Output is 32 bytes before input / free mem pointer. + // Failing ecrecover cannot be detected, so we clear output before the call. + m_context << u256(32); + utils().fetchFreeMemoryPointer(); + m_context << Instruction::SUB << Instruction::MLOAD; + } else if (!_functionType.returnParameterTypes().empty()) { utils().fetchFreeMemoryPointer(); diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index ea8bc1ba..553e5518 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -17,7 +17,7 @@ /** * @author Christian <c@ethdev.com> * @date 2015 - * LValues for use in the expresison compiler. + * LValues for use in the expression compiler. */ #include <libsolidity/codegen/LValue.h> diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index e8c3aa80..a2f979db 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -17,7 +17,7 @@ /** * @author Christian <c@ethdev.com> * @date 2015 - * LValues for use in the expresison compiler. + * LValues for use in the expression compiler. */ #pragma once diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt index 08a74f45..0d1f68a6 100644 --- a/libsolidity/grammar.txt +++ b/libsolidity/grammar.txt @@ -1,48 +1,102 @@ +SourceUnit = (ImportDirective | ContractDefinition)* + ContractDefinition = ( 'contract' | 'library' ) Identifier ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? '{' ContractPart* '}' -ContractPart = StateVariableDeclaration | StructDefinition | ModifierDefinition | FunctionDefinition | EnumDefinition + +ImportDirective = 'import' StringLiteral ('as' Identifier)? ';' + | 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';' + | 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';' + +ContractPart = StateVariableDeclaration | UsingForDeclaration + | StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )? + +StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' )? Identifier ('=' Expression)? ';' +UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration (';' VariableDeclaration)* )? '} -StateVariableDeclaration = TypeName ( 'public' | 'inheritable' | 'private' )? Identifier ';' + ( VariableDeclaration ';' (VariableDeclaration ';')* )? '}' ModifierDefinition = 'modifier' Identifier ParameterList? Block -FunctionDefinition = 'function' Identifier ParameterList - ( Identifier | 'constant' | 'external' | 'public' | 'inheritable' | 'private' )* +FunctionDefinition = 'function' Identifier? ParameterList + ( FunctionCall | Identifier | 'constant' | 'external' | 'public' | 'internal' | 'private' )* ( 'returns' ParameterList )? Block +EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';' EnumValue = Identifier -EnumDefinition = 'enum' '{' EnumValue (',' EnumValue)* '}' -ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' +EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}' + +IndexedParameterList = '(' ( TypeName 'indexed'? Identifier? (',' TypeName 'indexed'? Identifier?)* )? ')' +ParameterList = '(' ( TypeName Identifier? (',' TypeName Identifier?)* )? ')' + // semantic restriction: mappings and structs (recursively) containing mappings // are not allowed in argument lists VariableDeclaration = TypeName Identifier -TypeName = ElementaryTypeName | Identifier | Mapping | ArrayTypeName +TypeName = ElementaryTypeName | Identifier StorageLocation? | Mapping | ArrayTypeName Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' -ArrayTypeName = TypeName '[' (Expression)? ']' +ArrayTypeName = TypeName StorageLocation? '[' Expression? ']' +StorageLocation = 'memory' | 'storage' Block = '{' Statement* '}' -Statement = IfStatement | WhileStatement | Block | - ( Continue | Break | Return | VariableDefinition | ExpressionStatement ) ';' +Statement = IfStatement | WhileStatement | ForStatement | Block | PlaceholderStatement | + ( Continue | Break | Return | Throw | SimpleStatement | ExpressionStatement ) ';' -ExpressionStatement = Expression +ExpressionStatement = Expression | VariableDefinition IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? WhileStatement = 'while' '(' Expression ')' Statement -VardefOrExprStmt = Variabledefinition | ExpressionStatement -ForStatement = 'for' '(' (VardefOrExprStmt)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -Continue = 'continue' ';' -Break = 'break' ';' -Return = 'return' Expression? ';' -Throw = 'throw' Expression? ';' -VariableDefinition = VariableDeclaration ( = Expression )? ';' - -Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | NewExpression | IndexAccess | - MemberAccess | PrimaryExpression -// The expression syntax is actually much more complicated -Assignment = Expression (AssignmentOp Expression) -FunctionCall = Expression '(' Expression ( ',' Expression )* ')' +PlaceholderStatement = '_' +SimpleStatement = VariableDefinition | ExpressionStatement +ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement +Continue = 'continue' +Break = 'break' +Return = 'return' Expression? +Throw = 'throw' +VariableDefinition = VariableDeclaration ( '=' Expression )? + +// Precedence by order (see github.com/ethereum/solidity/pull/732) +Expression = + ( Expression ('++' | '--') | FunctionCall | IndexAccess | MemberAccess | '(' Expression ')' ) + | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression + | Expression '**' Expression + | Expression ('*' | '/' | '%') Expression + | Expression ('+' | '-') Expression + | Expression ('<<' | '>>' | '>>>') + | Expression '&' Expression + | Expression '^' Expression + | Expression '|' Expression + | Expression ('<' | '>' | '<=' | '>=') Expression + | Expression ('==' | '!=') Expression + | Expression '&&' Expression + | Expression '||' Expression + | Expression '?' Expression ':' Expression + | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression + | Expression? (',' Expression) + | PrimaryExpression + +PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | StringLiteral + +FunctionCall = Identifier '(' Expression? ( ',' Expression )* ')' NewExpression = 'new' Identifier MemberAccess = Expression '.' Identifier -IndexAccess = Expression '[' (Expresison)? ']' -PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' +IndexAccess = Expression '[' Expression? ']' + +BooleanLiteral = 'true' | 'false' +NumberLiteral = '0x'? [0-9]+ (' ' NumberUnit)? +NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' + | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' +StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' + +Identifier = [a-zA-Z_] [a-zA-Z_0-9]* + +ElementaryTypeName = 'address' | 'bool' | 'string' | 'var' + | Int | Uint | Byte | Fixed | Ufixed + +Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' + +Uint = 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' + +Byte = 'byte' | 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' + +Fixed = 'fixed' | 'fixed0x8' | 'fixed0x16' | 'fixed0x24' | 'fixed0x32' | 'fixed0x40' | 'fixed0x48' | 'fixed0x56' | 'fixed0x64' | 'fixed0x72' | 'fixed0x80' | 'fixed0x88' | 'fixed0x96' | 'fixed0x104' | 'fixed0x112' | 'fixed0x120' | 'fixed0x128' | 'fixed0x136' | 'fixed0x144' | 'fixed0x152' | 'fixed0x160' | 'fixed0x168' | 'fixed0x176' | 'fixed0x184' | 'fixed0x192' | 'fixed0x200' | 'fixed0x208' | 'fixed0x216' | 'fixed0x224' | 'fixed0x232' | 'fixed0x240' | 'fixed0x248' | 'fixed0x256' | 'fixed8x8' | 'fixed8x16' | 'fixed8x24' | 'fixed8x32' | 'fixed8x40' | 'fixed8x48' | 'fixed8x56' | 'fixed8x64' | 'fixed8x72' | 'fixed8x80' | 'fixed8x88' | 'fixed8x96' | 'fixed8x104' | 'fixed8x112' | 'fixed8x120' | 'fixed8x128' | 'fixed8x136' | 'fixed8x144' | 'fixed8x152' | 'fixed8x160' | 'fixed8x168' | 'fixed8x176' | 'fixed8x184' | 'fixed8x192' | 'fixed8x200' | 'fixed8x208' | 'fixed8x216' | 'fixed8x224' | 'fixed8x232' | 'fixed8x240' | 'fixed8x248' | 'fixed16x8' | 'fixed16x16' | 'fixed16x24' | 'fixed16x32' | 'fixed16x40' | 'fixed16x48' | 'fixed16x56' | 'fixed16x64' | 'fixed16x72' | 'fixed16x80' | 'fixed16x88' | 'fixed16x96' | 'fixed16x104' | 'fixed16x112' | 'fixed16x120' | 'fixed16x128' | 'fixed16x136' | 'fixed16x144' | 'fixed16x152' | 'fixed16x160' | 'fixed16x168' | 'fixed16x176' | 'fixed16x184' | 'fixed16x192' | 'fixed16x200' | 'fixed16x208' | 'fixed16x216' | 'fixed16x224' | 'fixed16x232' | 'fixed16x240' | 'fixed24x8' | 'fixed24x16' | 'fixed24x24' | 'fixed24x32' | 'fixed24x40' | 'fixed24x48' | 'fixed24x56' | 'fixed24x64' | 'fixed24x72' | 'fixed24x80' | 'fixed24x88' | 'fixed24x96' | 'fixed24x104' | 'fixed24x112' | 'fixed24x120' | 'fixed24x128' | 'fixed24x136' | 'fixed24x144' | 'fixed24x152' | 'fixed24x160' | 'fixed24x168' | 'fixed24x176' | 'fixed24x184' | 'fixed24x192' | 'fixed24x200' | 'fixed24x208' | 'fixed24x216' | 'fixed24x224' | 'fixed24x232' | 'fixed32x8' | 'fixed32x16' | 'fixed32x24' | 'fixed32x32' | 'fixed32x40' | 'fixed32x48' | 'fixed32x56' | 'fixed32x64' | 'fixed32x72' | 'fixed32x80' | 'fixed32x88' | 'fixed32x96' | 'fixed32x104' | 'fixed32x112' | 'fixed32x120' | 'fixed32x128' | 'fixed32x136' | 'fixed32x144' | 'fixed32x152' | 'fixed32x160' | 'fixed32x168' | 'fixed32x176' | 'fixed32x184' | 'fixed32x192' | 'fixed32x200' | 'fixed32x208' | 'fixed32x216' | 'fixed32x224' | 'fixed40x8' | 'fixed40x16' | 'fixed40x24' | 'fixed40x32' | 'fixed40x40' | 'fixed40x48' | 'fixed40x56' | 'fixed40x64' | 'fixed40x72' | 'fixed40x80' | 'fixed40x88' | 'fixed40x96' | 'fixed40x104' | 'fixed40x112' | 'fixed40x120' | 'fixed40x128' | 'fixed40x136' | 'fixed40x144' | 'fixed40x152' | 'fixed40x160' | 'fixed40x168' | 'fixed40x176' | 'fixed40x184' | 'fixed40x192' | 'fixed40x200' | 'fixed40x208' | 'fixed40x216' | 'fixed48x8' | 'fixed48x16' | 'fixed48x24' | 'fixed48x32' | 'fixed48x40' | 'fixed48x48' | 'fixed48x56' | 'fixed48x64' | 'fixed48x72' | 'fixed48x80' | 'fixed48x88' | 'fixed48x96' | 'fixed48x104' | 'fixed48x112' | 'fixed48x120' | 'fixed48x128' | 'fixed48x136' | 'fixed48x144' | 'fixed48x152' | 'fixed48x160' | 'fixed48x168' | 'fixed48x176' | 'fixed48x184' | 'fixed48x192' | 'fixed48x200' | 'fixed48x208' | 'fixed56x8' | 'fixed56x16' | 'fixed56x24' | 'fixed56x32' | 'fixed56x40' | 'fixed56x48' | 'fixed56x56' | 'fixed56x64' | 'fixed56x72' | 'fixed56x80' | 'fixed56x88' | 'fixed56x96' | 'fixed56x104' | 'fixed56x112' | 'fixed56x120' | 'fixed56x128' | 'fixed56x136' | 'fixed56x144' | 'fixed56x152' | 'fixed56x160' | 'fixed56x168' | 'fixed56x176' | 'fixed56x184' | 'fixed56x192' | 'fixed56x200' | 'fixed64x8' | 'fixed64x16' | 'fixed64x24' | 'fixed64x32' | 'fixed64x40' | 'fixed64x48' | 'fixed64x56' | 'fixed64x64' | 'fixed64x72' | 'fixed64x80' | 'fixed64x88' | 'fixed64x96' | 'fixed64x104' | 'fixed64x112' | 'fixed64x120' | 'fixed64x128' | 'fixed64x136' | 'fixed64x144' | 'fixed64x152' | 'fixed64x160' | 'fixed64x168' | 'fixed64x176' | 'fixed64x184' | 'fixed64x192' | 'fixed72x8' | 'fixed72x16' | 'fixed72x24' | 'fixed72x32' | 'fixed72x40' | 'fixed72x48' | 'fixed72x56' | 'fixed72x64' | 'fixed72x72' | 'fixed72x80' | 'fixed72x88' | 'fixed72x96' | 'fixed72x104' | 'fixed72x112' | 'fixed72x120' | 'fixed72x128' | 'fixed72x136' | 'fixed72x144' | 'fixed72x152' | 'fixed72x160' | 'fixed72x168' | 'fixed72x176' | 'fixed72x184' | 'fixed80x8' | 'fixed80x16' | 'fixed80x24' | 'fixed80x32' | 'fixed80x40' | 'fixed80x48' | 'fixed80x56' | 'fixed80x64' | 'fixed80x72' | 'fixed80x80' | 'fixed80x88' | 'fixed80x96' | 'fixed80x104' | 'fixed80x112' | 'fixed80x120' | 'fixed80x128' | 'fixed80x136' | 'fixed80x144' | 'fixed80x152' | 'fixed80x160' | 'fixed80x168' | 'fixed80x176' | 'fixed88x8' | 'fixed88x16' | 'fixed88x24' | 'fixed88x32' | 'fixed88x40' | 'fixed88x48' | 'fixed88x56' | 'fixed88x64' | 'fixed88x72' | 'fixed88x80' | 'fixed88x88' | 'fixed88x96' | 'fixed88x104' | 'fixed88x112' | 'fixed88x120' | 'fixed88x128' | 'fixed88x136' | 'fixed88x144' | 'fixed88x152' | 'fixed88x160' | 'fixed88x168' | 'fixed96x8' | 'fixed96x16' | 'fixed96x24' | 'fixed96x32' | 'fixed96x40' | 'fixed96x48' | 'fixed96x56' | 'fixed96x64' | 'fixed96x72' | 'fixed96x80' | 'fixed96x88' | 'fixed96x96' | 'fixed96x104' | 'fixed96x112' | 'fixed96x120' | 'fixed96x128' | 'fixed96x136' | 'fixed96x144' | 'fixed96x152' | 'fixed96x160' | 'fixed104x8' | 'fixed104x16' | 'fixed104x24' | 'fixed104x32' | 'fixed104x40' | 'fixed104x48' | 'fixed104x56' | 'fixed104x64' | 'fixed104x72' | 'fixed104x80' | 'fixed104x88' | 'fixed104x96' | 'fixed104x104' | 'fixed104x112' | 'fixed104x120' | 'fixed104x128' | 'fixed104x136' | 'fixed104x144' | 'fixed104x152' | 'fixed112x8' | 'fixed112x16' | 'fixed112x24' | 'fixed112x32' | 'fixed112x40' | 'fixed112x48' | 'fixed112x56' | 'fixed112x64' | 'fixed112x72' | 'fixed112x80' | 'fixed112x88' | 'fixed112x96' | 'fixed112x104' | 'fixed112x112' | 'fixed112x120' | 'fixed112x128' | 'fixed112x136' | 'fixed112x144' | 'fixed120x8' | 'fixed120x16' | 'fixed120x24' | 'fixed120x32' | 'fixed120x40' | 'fixed120x48' | 'fixed120x56' | 'fixed120x64' | 'fixed120x72' | 'fixed120x80' | 'fixed120x88' | 'fixed120x96' | 'fixed120x104' | 'fixed120x112' | 'fixed120x120' | 'fixed120x128' | 'fixed120x136' | 'fixed128x8' | 'fixed128x16' | 'fixed128x24' | 'fixed128x32' | 'fixed128x40' | 'fixed128x48' | 'fixed128x56' | 'fixed128x64' | 'fixed128x72' | 'fixed128x80' | 'fixed128x88' | 'fixed128x96' | 'fixed128x104' | 'fixed128x112' | 'fixed128x120' | 'fixed128x128' | 'fixed136x8' | 'fixed136x16' | 'fixed136x24' | 'fixed136x32' | 'fixed136x40' | 'fixed136x48' | 'fixed136x56' | 'fixed136x64' | 'fixed136x72' | 'fixed136x80' | 'fixed136x88' | 'fixed136x96' | 'fixed136x104' | 'fixed136x112' | 'fixed136x120' | 'fixed144x8' | 'fixed144x16' | 'fixed144x24' | 'fixed144x32' | 'fixed144x40' | 'fixed144x48' | 'fixed144x56' | 'fixed144x64' | 'fixed144x72' | 'fixed144x80' | 'fixed144x88' | 'fixed144x96' | 'fixed144x104' | 'fixed144x112' | 'fixed152x8' | 'fixed152x16' | 'fixed152x24' | 'fixed152x32' | 'fixed152x40' | 'fixed152x48' | 'fixed152x56' | 'fixed152x64' | 'fixed152x72' | 'fixed152x80' | 'fixed152x88' | 'fixed152x96' | 'fixed152x104' | 'fixed160x8' | 'fixed160x16' | 'fixed160x24' | 'fixed160x32' | 'fixed160x40' | 'fixed160x48' | 'fixed160x56' | 'fixed160x64' | 'fixed160x72' | 'fixed160x80' | 'fixed160x88' | 'fixed160x96' | 'fixed168x8' | 'fixed168x16' | 'fixed168x24' | 'fixed168x32' | 'fixed168x40' | 'fixed168x48' | 'fixed168x56' | 'fixed168x64' | 'fixed168x72' | 'fixed168x80' | 'fixed168x88' | 'fixed176x8' | 'fixed176x16' | 'fixed176x24' | 'fixed176x32' | 'fixed176x40' | 'fixed176x48' | 'fixed176x56' | 'fixed176x64' | 'fixed176x72' | 'fixed176x80' | 'fixed184x8' | 'fixed184x16' | 'fixed184x24' | 'fixed184x32' | 'fixed184x40' | 'fixed184x48' | 'fixed184x56' | 'fixed184x64' | 'fixed184x72' | 'fixed192x8' | 'fixed192x16' | 'fixed192x24' | 'fixed192x32' | 'fixed192x40' | 'fixed192x48' | 'fixed192x56' | 'fixed192x64' | 'fixed200x8' | 'fixed200x16' | 'fixed200x24' | 'fixed200x32' | 'fixed200x40' | 'fixed200x48' | 'fixed200x56' | 'fixed208x8' | 'fixed208x16' | 'fixed208x24' | 'fixed208x32' | 'fixed208x40' | 'fixed208x48' | 'fixed216x8' | 'fixed216x16' | 'fixed216x24' | 'fixed216x32' | 'fixed216x40' | 'fixed224x8' | 'fixed224x16' | 'fixed224x24' | 'fixed224x32' | 'fixed232x8' | 'fixed232x16' | 'fixed232x24' | 'fixed240x8' | 'fixed240x16' | 'fixed248x8' + +Ufixed = 'ufixed' | 'ufixed0x8' | 'ufixed0x16' | 'ufixed0x24' | 'ufixed0x32' | 'ufixed0x40' | 'ufixed0x48' | 'ufixed0x56' | 'ufixed0x64' | 'ufixed0x72' | 'ufixed0x80' | 'ufixed0x88' | 'ufixed0x96' | 'ufixed0x104' | 'ufixed0x112' | 'ufixed0x120' | 'ufixed0x128' | 'ufixed0x136' | 'ufixed0x144' | 'ufixed0x152' | 'ufixed0x160' | 'ufixed0x168' | 'ufixed0x176' | 'ufixed0x184' | 'ufixed0x192' | 'ufixed0x200' | 'ufixed0x208' | 'ufixed0x216' | 'ufixed0x224' | 'ufixed0x232' | 'ufixed0x240' | 'ufixed0x248' | 'ufixed0x256' | 'ufixed8x8' | 'ufixed8x16' | 'ufixed8x24' | 'ufixed8x32' | 'ufixed8x40' | 'ufixed8x48' | 'ufixed8x56' | 'ufixed8x64' | 'ufixed8x72' | 'ufixed8x80' | 'ufixed8x88' | 'ufixed8x96' | 'ufixed8x104' | 'ufixed8x112' | 'ufixed8x120' | 'ufixed8x128' | 'ufixed8x136' | 'ufixed8x144' | 'ufixed8x152' | 'ufixed8x160' | 'ufixed8x168' | 'ufixed8x176' | 'ufixed8x184' | 'ufixed8x192' | 'ufixed8x200' | 'ufixed8x208' | 'ufixed8x216' | 'ufixed8x224' | 'ufixed8x232' | 'ufixed8x240' | 'ufixed8x248' | 'ufixed16x8' | 'ufixed16x16' | 'ufixed16x24' | 'ufixed16x32' | 'ufixed16x40' | 'ufixed16x48' | 'ufixed16x56' | 'ufixed16x64' | 'ufixed16x72' | 'ufixed16x80' | 'ufixed16x88' | 'ufixed16x96' | 'ufixed16x104' | 'ufixed16x112' | 'ufixed16x120' | 'ufixed16x128' | 'ufixed16x136' | 'ufixed16x144' | 'ufixed16x152' | 'ufixed16x160' | 'ufixed16x168' | 'ufixed16x176' | 'ufixed16x184' | 'ufixed16x192' | 'ufixed16x200' | 'ufixed16x208' | 'ufixed16x216' | 'ufixed16x224' | 'ufixed16x232' | 'ufixed16x240' | 'ufixed24x8' | 'ufixed24x16' | 'ufixed24x24' | 'ufixed24x32' | 'ufixed24x40' | 'ufixed24x48' | 'ufixed24x56' | 'ufixed24x64' | 'ufixed24x72' | 'ufixed24x80' | 'ufixed24x88' | 'ufixed24x96' | 'ufixed24x104' | 'ufixed24x112' | 'ufixed24x120' | 'ufixed24x128' | 'ufixed24x136' | 'ufixed24x144' | 'ufixed24x152' | 'ufixed24x160' | 'ufixed24x168' | 'ufixed24x176' | 'ufixed24x184' | 'ufixed24x192' | 'ufixed24x200' | 'ufixed24x208' | 'ufixed24x216' | 'ufixed24x224' | 'ufixed24x232' | 'ufixed32x8' | 'ufixed32x16' | 'ufixed32x24' | 'ufixed32x32' | 'ufixed32x40' | 'ufixed32x48' | 'ufixed32x56' | 'ufixed32x64' | 'ufixed32x72' | 'ufixed32x80' | 'ufixed32x88' | 'ufixed32x96' | 'ufixed32x104' | 'ufixed32x112' | 'ufixed32x120' | 'ufixed32x128' | 'ufixed32x136' | 'ufixed32x144' | 'ufixed32x152' | 'ufixed32x160' | 'ufixed32x168' | 'ufixed32x176' | 'ufixed32x184' | 'ufixed32x192' | 'ufixed32x200' | 'ufixed32x208' | 'ufixed32x216' | 'ufixed32x224' | 'ufixed40x8' | 'ufixed40x16' | 'ufixed40x24' | 'ufixed40x32' | 'ufixed40x40' | 'ufixed40x48' | 'ufixed40x56' | 'ufixed40x64' | 'ufixed40x72' | 'ufixed40x80' | 'ufixed40x88' | 'ufixed40x96' | 'ufixed40x104' | 'ufixed40x112' | 'ufixed40x120' | 'ufixed40x128' | 'ufixed40x136' | 'ufixed40x144' | 'ufixed40x152' | 'ufixed40x160' | 'ufixed40x168' | 'ufixed40x176' | 'ufixed40x184' | 'ufixed40x192' | 'ufixed40x200' | 'ufixed40x208' | 'ufixed40x216' | 'ufixed48x8' | 'ufixed48x16' | 'ufixed48x24' | 'ufixed48x32' | 'ufixed48x40' | 'ufixed48x48' | 'ufixed48x56' | 'ufixed48x64' | 'ufixed48x72' | 'ufixed48x80' | 'ufixed48x88' | 'ufixed48x96' | 'ufixed48x104' | 'ufixed48x112' | 'ufixed48x120' | 'ufixed48x128' | 'ufixed48x136' | 'ufixed48x144' | 'ufixed48x152' | 'ufixed48x160' | 'ufixed48x168' | 'ufixed48x176' | 'ufixed48x184' | 'ufixed48x192' | 'ufixed48x200' | 'ufixed48x208' | 'ufixed56x8' | 'ufixed56x16' | 'ufixed56x24' | 'ufixed56x32' | 'ufixed56x40' | 'ufixed56x48' | 'ufixed56x56' | 'ufixed56x64' | 'ufixed56x72' | 'ufixed56x80' | 'ufixed56x88' | 'ufixed56x96' | 'ufixed56x104' | 'ufixed56x112' | 'ufixed56x120' | 'ufixed56x128' | 'ufixed56x136' | 'ufixed56x144' | 'ufixed56x152' | 'ufixed56x160' | 'ufixed56x168' | 'ufixed56x176' | 'ufixed56x184' | 'ufixed56x192' | 'ufixed56x200' | 'ufixed64x8' | 'ufixed64x16' | 'ufixed64x24' | 'ufixed64x32' | 'ufixed64x40' | 'ufixed64x48' | 'ufixed64x56' | 'ufixed64x64' | 'ufixed64x72' | 'ufixed64x80' | 'ufixed64x88' | 'ufixed64x96' | 'ufixed64x104' | 'ufixed64x112' | 'ufixed64x120' | 'ufixed64x128' | 'ufixed64x136' | 'ufixed64x144' | 'ufixed64x152' | 'ufixed64x160' | 'ufixed64x168' | 'ufixed64x176' | 'ufixed64x184' | 'ufixed64x192' | 'ufixed72x8' | 'ufixed72x16' | 'ufixed72x24' | 'ufixed72x32' | 'ufixed72x40' | 'ufixed72x48' | 'ufixed72x56' | 'ufixed72x64' | 'ufixed72x72' | 'ufixed72x80' | 'ufixed72x88' | 'ufixed72x96' | 'ufixed72x104' | 'ufixed72x112' | 'ufixed72x120' | 'ufixed72x128' | 'ufixed72x136' | 'ufixed72x144' | 'ufixed72x152' | 'ufixed72x160' | 'ufixed72x168' | 'ufixed72x176' | 'ufixed72x184' | 'ufixed80x8' | 'ufixed80x16' | 'ufixed80x24' | 'ufixed80x32' | 'ufixed80x40' | 'ufixed80x48' | 'ufixed80x56' | 'ufixed80x64' | 'ufixed80x72' | 'ufixed80x80' | 'ufixed80x88' | 'ufixed80x96' | 'ufixed80x104' | 'ufixed80x112' | 'ufixed80x120' | 'ufixed80x128' | 'ufixed80x136' | 'ufixed80x144' | 'ufixed80x152' | 'ufixed80x160' | 'ufixed80x168' | 'ufixed80x176' | 'ufixed88x8' | 'ufixed88x16' | 'ufixed88x24' | 'ufixed88x32' | 'ufixed88x40' | 'ufixed88x48' | 'ufixed88x56' | 'ufixed88x64' | 'ufixed88x72' | 'ufixed88x80' | 'ufixed88x88' | 'ufixed88x96' | 'ufixed88x104' | 'ufixed88x112' | 'ufixed88x120' | 'ufixed88x128' | 'ufixed88x136' | 'ufixed88x144' | 'ufixed88x152' | 'ufixed88x160' | 'ufixed88x168' | 'ufixed96x8' | 'ufixed96x16' | 'ufixed96x24' | 'ufixed96x32' | 'ufixed96x40' | 'ufixed96x48' | 'ufixed96x56' | 'ufixed96x64' | 'ufixed96x72' | 'ufixed96x80' | 'ufixed96x88' | 'ufixed96x96' | 'ufixed96x104' | 'ufixed96x112' | 'ufixed96x120' | 'ufixed96x128' | 'ufixed96x136' | 'ufixed96x144' | 'ufixed96x152' | 'ufixed96x160' | 'ufixed104x8' | 'ufixed104x16' | 'ufixed104x24' | 'ufixed104x32' | 'ufixed104x40' | 'ufixed104x48' | 'ufixed104x56' | 'ufixed104x64' | 'ufixed104x72' | 'ufixed104x80' | 'ufixed104x88' | 'ufixed104x96' | 'ufixed104x104' | 'ufixed104x112' | 'ufixed104x120' | 'ufixed104x128' | 'ufixed104x136' | 'ufixed104x144' | 'ufixed104x152' | 'ufixed112x8' | 'ufixed112x16' | 'ufixed112x24' | 'ufixed112x32' | 'ufixed112x40' | 'ufixed112x48' | 'ufixed112x56' | 'ufixed112x64' | 'ufixed112x72' | 'ufixed112x80' | 'ufixed112x88' | 'ufixed112x96' | 'ufixed112x104' | 'ufixed112x112' | 'ufixed112x120' | 'ufixed112x128' | 'ufixed112x136' | 'ufixed112x144' | 'ufixed120x8' | 'ufixed120x16' | 'ufixed120x24' | 'ufixed120x32' | 'ufixed120x40' | 'ufixed120x48' | 'ufixed120x56' | 'ufixed120x64' | 'ufixed120x72' | 'ufixed120x80' | 'ufixed120x88' | 'ufixed120x96' | 'ufixed120x104' | 'ufixed120x112' | 'ufixed120x120' | 'ufixed120x128' | 'ufixed120x136' | 'ufixed128x8' | 'ufixed128x16' | 'ufixed128x24' | 'ufixed128x32' | 'ufixed128x40' | 'ufixed128x48' | 'ufixed128x56' | 'ufixed128x64' | 'ufixed128x72' | 'ufixed128x80' | 'ufixed128x88' | 'ufixed128x96' | 'ufixed128x104' | 'ufixed128x112' | 'ufixed128x120' | 'ufixed128x128' | 'ufixed136x8' | 'ufixed136x16' | 'ufixed136x24' | 'ufixed136x32' | 'ufixed136x40' | 'ufixed136x48' | 'ufixed136x56' | 'ufixed136x64' | 'ufixed136x72' | 'ufixed136x80' | 'ufixed136x88' | 'ufixed136x96' | 'ufixed136x104' | 'ufixed136x112' | 'ufixed136x120' | 'ufixed144x8' | 'ufixed144x16' | 'ufixed144x24' | 'ufixed144x32' | 'ufixed144x40' | 'ufixed144x48' | 'ufixed144x56' | 'ufixed144x64' | 'ufixed144x72' | 'ufixed144x80' | 'ufixed144x88' | 'ufixed144x96' | 'ufixed144x104' | 'ufixed144x112' | 'ufixed152x8' | 'ufixed152x16' | 'ufixed152x24' | 'ufixed152x32' | 'ufixed152x40' | 'ufixed152x48' | 'ufixed152x56' | 'ufixed152x64' | 'ufixed152x72' | 'ufixed152x80' | 'ufixed152x88' | 'ufixed152x96' | 'ufixed152x104' | 'ufixed160x8' | 'ufixed160x16' | 'ufixed160x24' | 'ufixed160x32' | 'ufixed160x40' | 'ufixed160x48' | 'ufixed160x56' | 'ufixed160x64' | 'ufixed160x72' | 'ufixed160x80' | 'ufixed160x88' | 'ufixed160x96' | 'ufixed168x8' | 'ufixed168x16' | 'ufixed168x24' | 'ufixed168x32' | 'ufixed168x40' | 'ufixed168x48' | 'ufixed168x56' | 'ufixed168x64' | 'ufixed168x72' | 'ufixed168x80' | 'ufixed168x88' | 'ufixed176x8' | 'ufixed176x16' | 'ufixed176x24' | 'ufixed176x32' | 'ufixed176x40' | 'ufixed176x48' | 'ufixed176x56' | 'ufixed176x64' | 'ufixed176x72' | 'ufixed176x80' | 'ufixed184x8' | 'ufixed184x16' | 'ufixed184x24' | 'ufixed184x32' | 'ufixed184x40' | 'ufixed184x48' | 'ufixed184x56' | 'ufixed184x64' | 'ufixed184x72' | 'ufixed192x8' | 'ufixed192x16' | 'ufixed192x24' | 'ufixed192x32' | 'ufixed192x40' | 'ufixed192x48' | 'ufixed192x56' | 'ufixed192x64' | 'ufixed200x8' | 'ufixed200x16' | 'ufixed200x24' | 'ufixed200x32' | 'ufixed200x40' | 'ufixed200x48' | 'ufixed200x56' | 'ufixed208x8' | 'ufixed208x16' | 'ufixed208x24' | 'ufixed208x32' | 'ufixed208x40' | 'ufixed208x48' | 'ufixed216x8' | 'ufixed216x16' | 'ufixed216x24' | 'ufixed216x32' | 'ufixed216x40' | 'ufixed224x8' | 'ufixed224x16' | 'ufixed224x24' | 'ufixed224x32' | 'ufixed232x8' | 'ufixed232x16' | 'ufixed232x24' | 'ufixed240x8' | 'ufixed240x16' | 'ufixed248x8' diff --git a/libsolidity/inlineasm/AsmStack.cpp b/libsolidity/inlineasm/AsmStack.cpp index c891678b..11c6e28f 100644 --- a/libsolidity/inlineasm/AsmStack.cpp +++ b/libsolidity/inlineasm/AsmStack.cpp @@ -24,6 +24,7 @@ #include <memory> #include <libevmasm/Assembly.h> #include <libevmasm/SourceLocation.h> +#include <libsolidity/parsing/Scanner.h> #include <libsolidity/inlineasm/AsmParser.h> #include <libsolidity/inlineasm/AsmCodeGen.h> @@ -32,7 +33,7 @@ using namespace dev; using namespace dev::solidity; using namespace dev::solidity::assembly; -bool InlineAssemblyStack::parse(const std::shared_ptr<Scanner>& _scanner) +bool InlineAssemblyStack::parse(shared_ptr<Scanner> const& _scanner) { m_parserResult = make_shared<Block>(); Parser parser(m_errors); @@ -49,3 +50,22 @@ eth::Assembly InlineAssemblyStack::assemble() return codeGen.assemble(); } +bool InlineAssemblyStack::parseAndAssemble( + string const& _input, + eth::Assembly& _assembly, + CodeGenerator::IdentifierAccess const& _identifierAccess +) +{ + ErrorList errors; + auto scanner = make_shared<Scanner>(CharStream(_input), "--CODEGEN--"); + auto parserResult = Parser(errors).parse(scanner); + if (!errors.empty()) + return false; + + CodeGenerator(*parserResult, errors).assemble(_assembly, _identifierAccess); + + // At this point, the assembly might be messed up, but we should throw an + // internal compiler error anyway. + return errors.empty(); +} + diff --git a/libsolidity/inlineasm/AsmStack.h b/libsolidity/inlineasm/AsmStack.h index 8a860e46..521f5fe7 100644 --- a/libsolidity/inlineasm/AsmStack.h +++ b/libsolidity/inlineasm/AsmStack.h @@ -25,6 +25,7 @@ #include <string> #include <functional> #include <libsolidity/interface/Exceptions.h> +#include <libsolidity/inlineasm/AsmCodeGen.h> namespace dev { @@ -47,6 +48,13 @@ public: bool parse(std::shared_ptr<Scanner> const& _scanner); eth::Assembly assemble(); + /// Parse and assemble a string in one run - for use in Solidity code generation itself. + bool parseAndAssemble( + std::string const& _input, + eth::Assembly& _assembly, + CodeGenerator::IdentifierAccess const& _identifierAccess = CodeGenerator::IdentifierAccess() + ); + ErrorList const& errors() const { return m_errors; } private: diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index f7982872..0e5ead2b 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -358,11 +358,6 @@ string const& CompilerStack::interface(string const& _contractName) const return metadata(_contractName, DocumentationType::ABIInterface); } -string const& CompilerStack::solidityInterface(string const& _contractName) const -{ - return metadata(_contractName, DocumentationType::ABISolidityInterface); -} - string const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const { if (!m_parseSuccessful) @@ -383,9 +378,6 @@ string const& CompilerStack::metadata(string const& _contractName, Documentation case DocumentationType::ABIInterface: doc = ¤tContract.interface; break; - case DocumentationType::ABISolidityInterface: - doc = ¤tContract.solidityInterface; - break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Illegal documentation type.")); } diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h index a4b8447f..b3c4450c 100644 --- a/libsolidity/interface/CompilerStack.h +++ b/libsolidity/interface/CompilerStack.h @@ -63,8 +63,7 @@ enum class DocumentationType: uint8_t { NatspecUser = 1, NatspecDev, - ABIInterface, - ABISolidityInterface + ABIInterface }; /** @@ -167,9 +166,6 @@ public: /// @returns a string representing the contract interface in JSON. /// Prerequisite: Successful call to parse or compile. std::string const& interface(std::string const& _contractName = "") const; - /// @returns a string representing the contract interface in Solidity. - /// Prerequisite: Successful call to parse or compile. - std::string const& solidityInterface(std::string const& _contractName = "") const; /// @returns a string representing the contract's documentation in JSON. /// Prerequisite: Successful call to parse or compile. /// @param type The type of the documentation to get. @@ -219,7 +215,6 @@ private: eth::LinkerObject runtimeObject; eth::LinkerObject cloneObject; mutable std::unique_ptr<std::string const> interface; - mutable std::unique_ptr<std::string const> solidityInterface; mutable std::unique_ptr<std::string const> userDocumentation; mutable std::unique_ptr<std::string const> devDocumentation; mutable std::unique_ptr<std::string const> sourceMapping; diff --git a/libsolidity/interface/InterfaceHandler.cpp b/libsolidity/interface/InterfaceHandler.cpp index e254137f..f5c10356 100644 --- a/libsolidity/interface/InterfaceHandler.cpp +++ b/libsolidity/interface/InterfaceHandler.cpp @@ -21,8 +21,6 @@ string InterfaceHandler::documentation( return devDocumentation(_contractDef); case DocumentationType::ABIInterface: return abiInterface(_contractDef); - case DocumentationType::ABISolidityInterface: - return ABISolidityInterface(_contractDef); } BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type")); @@ -98,74 +96,6 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef) return Json::FastWriter().write(abi); } -string InterfaceHandler::ABISolidityInterface(ContractDefinition const& _contractDef) -{ - string ret = (_contractDef.isLibrary() ? "library " : "contract ") + _contractDef.name() + "{"; - - auto populateParameters = [](vector<string> const& _paramNames, vector<string> const& _paramTypes) - { - string ret = "("; - for (size_t i = 0; i < _paramNames.size(); ++i) - ret += _paramTypes[i] + " " + _paramNames[i] + ","; - if (ret.size() != 1) - ret.pop_back(); - return ret + ")"; - }; - // If this is a library, include all its enum and struct types. Should be more intelligent - // in the future and check what is actually used (it might even use types from other libraries - // or contracts or in the global scope). - if (_contractDef.isLibrary()) - { - for (auto const& stru: _contractDef.definedStructs()) - { - ret += "struct " + stru->name() + "{"; - for (ASTPointer<VariableDeclaration> const& _member: stru->members()) - ret += _member->type()->canonicalName(false) + " " + _member->name() + ";"; - ret += "}"; - } - for (auto const& enu: _contractDef.definedEnums()) - { - ret += "enum " + enu->name() + "{"; - for (ASTPointer<EnumValue> const& val: enu->members()) - ret += val->name() + ","; - if (ret.back() == ',') - ret.pop_back(); - ret += "}"; - } - } - if (_contractDef.constructor()) - { - auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType(); - solAssert(!!externalFunction, ""); - ret += - "function " + - _contractDef.name() + - populateParameters( - externalFunction->parameterNames(), - externalFunction->parameterTypeNames(_contractDef.isLibrary()) - ) + - ";"; - } - for (auto const& it: _contractDef.interfaceFunctions()) - { - ret += "function " + it.second->declaration().name() + - populateParameters( - it.second->parameterNames(), - it.second->parameterTypeNames(_contractDef.isLibrary()) - ) + (it.second->isConstant() ? "constant " : ""); - if (it.second->returnParameterTypes().size()) - ret += "returns" + populateParameters( - it.second->returnParameterNames(), - it.second->returnParameterTypeNames(_contractDef.isLibrary()) - ); - else if (ret.back() == ' ') - ret.pop_back(); - ret += ";"; - } - - return ret + "}"; -} - string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef) { Json::Value doc; diff --git a/libsolidity/interface/InterfaceHandler.h b/libsolidity/interface/InterfaceHandler.h index 3e0a1660..54199e4e 100644 --- a/libsolidity/interface/InterfaceHandler.h +++ b/libsolidity/interface/InterfaceHandler.h @@ -73,7 +73,6 @@ public: /// @param _contractDef The contract definition /// @return A string with the json representation of the contract's ABI Interface static std::string abiInterface(ContractDefinition const& _contractDef); - static std::string ABISolidityInterface(ContractDefinition const& _contractDef); /// Get the User documentation of the contract /// @param _contractDef The contract definition /// @return A string with the json representation of the contract's user documentation diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp index d730210a..603f3e42 100644 --- a/libsolidity/parsing/Scanner.cpp +++ b/libsolidity/parsing/Scanner.cpp @@ -591,7 +591,23 @@ void Scanner::scanToken() break; default: if (isIdentifierStart(m_char)) + { tie(token, m, n) = scanIdentifierOrKeyword(); + + // Special case for hexademical literals + if (token == Token::Hex) + { + // reset + m = 0; + n = 0; + + // Special quoted hex string must follow + if (m_char == '"' || m_char == '\'') + token = scanHexString(); + else + token = Token::Illegal; + } + } else if (isDecimalDigit(m_char)) token = scanNumber(); else if (skipWhitespace()) @@ -684,6 +700,25 @@ Token::Value Scanner::scanString() return Token::StringLiteral; } +Token::Value Scanner::scanHexString() +{ + char const quote = m_char; + advance(); // consume quote + LiteralScope literal(this, LITERAL_TYPE_STRING); + while (m_char != quote && !isSourcePastEndOfInput() && !isLineTerminator(m_char)) + { + char c = m_char; + if (!scanHexByte(c)) + return Token::Illegal; + addLiteralChar(c); + } + if (m_char != quote) + return Token::Illegal; + literal.complete(); + advance(); // consume quote + return Token::StringLiteral; +} + void Scanner::scanDecimalDigits() { while (isDecimalDigit(m_char)) diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h index 708adf8f..36cba112 100644 --- a/libsolidity/parsing/Scanner.h +++ b/libsolidity/parsing/Scanner.h @@ -203,6 +203,7 @@ private: std::tuple<Token::Value, unsigned, unsigned> scanIdentifierOrKeyword(); Token::Value scanString(); + Token::Value scanHexString(); Token::Value scanSingleLineDocComment(); Token::Value scanMultiLineDocComment(); /// Scans a slash '/' and depending on the characters returns the appropriate token diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 581df3a5..007baef4 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -141,6 +141,7 @@ namespace solidity \ /* Keywords */ \ K(Anonymous, "anonymous", 0) \ + K(As, "as", 0) \ K(Assembly, "assembly", 0) \ K(Break, "break", 0) \ K(Const, "constant", 0) \ @@ -154,6 +155,7 @@ namespace solidity K(External, "external", 0) \ K(For, "for", 0) \ K(Function, "function", 0) \ + K(Hex, "hex", 0) \ K(If, "if", 0) \ K(Indexed, "indexed", 0) \ K(Internal, "internal", 0) \ @@ -171,6 +173,7 @@ namespace solidity K(Storage, "storage", 0) \ K(Struct, "struct", 0) \ K(Throw, "throw", 0) \ + K(Using, "using", 0) \ K(Var, "var", 0) \ K(While, "while", 0) \ \ @@ -185,7 +188,6 @@ namespace solidity K(SubDay, "days", 0) \ K(SubWeek, "weeks", 0) \ K(SubYear, "years", 0) \ - K(After, "after", 0) \ /* type keywords*/ \ K(Int, "int", 0) \ K(UInt, "uint", 0) \ @@ -215,21 +217,24 @@ namespace solidity T(Identifier, NULL, 0) \ \ /* Keywords reserved for future use. */ \ - K(As, "as", 0) \ + K(Abstract, "abstract", 0) \ + K(After, "after", 0) \ K(Case, "case", 0) \ K(Catch, "catch", 0) \ K(Final, "final", 0) \ K(In, "in", 0) \ K(Inline, "inline", 0) \ + K(Interface, "interface", 0) \ K(Let, "let", 0) \ K(Match, "match", 0) \ K(Of, "of", 0) \ + K(Payable, "payable", 0) \ K(Relocatable, "relocatable", 0) \ + K(Static, "static", 0) \ K(Switch, "switch", 0) \ K(Try, "try", 0) \ K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ - K(Using, "using", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ @@ -277,7 +282,7 @@ public: static bool isBitOp(Value op) { return (BitOr <= op && op <= BitAnd) || op == BitNot; } static bool isBooleanOp(Value op) { return (Or <= op && op <= And) || op == Not; } - static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub || op == After; } + static bool isUnaryOp(Value op) { return (Not <= op && op <= Delete) || op == Add || op == Sub; } static bool isCountOp(Value op) { return op == Inc || op == Dec; } static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh index bf9bd9b2..4fb948ed 100755 --- a/scripts/install_deps.sh +++ b/scripts/install_deps.sh @@ -1,7 +1,7 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh #------------------------------------------------------------------------------ -# Bash script for installing pre-requisite packages for solidity on a +# Shell script for installing pre-requisite packages for solidity on a # variety of Linux and other UNIX-derived platforms. # # This is an "infrastucture-as-code" alternative to the manual build @@ -12,17 +12,9 @@ # flow for all supported operating systems: # # - git clone --recursive -# - ./install_deps.sh +# - ./scripts/install_deps.sh # - cmake && make # -# At the time of writing we are assuming that 'lsb_release' is present for all -# Linux distros, which is not a valid assumption. We will need a variety of -# approaches to actually get this working across all the distros which people -# are using. -# -# See http://unix.stackexchange.com/questions/92199/how-can-i-reliably-get-the-operating-systems-name -# for some more background on this common problem. -# # TODO - There is no support here yet for cross-builds in any form, only # native builds. Expanding the functionality here to cover the mobile, # wearable and SBC platforms covered by doublethink and EthEmbedded would @@ -55,6 +47,19 @@ # Check for 'uname' and abort if it is not available. uname -v > /dev/null 2>&1 || { echo >&2 "ERROR - solidity requires 'uname' to identify the platform."; exit 1; } +# See http://unix.stackexchange.com/questions/92199/how-can-i-reliably-get-the-operating-systems-name +detect_linux_distro() { + if [ $(command -v lsb_release) ]; then + DISTRO=$(lsb_release -is) + elif [ -f /etc/os-release ]; then + # extract 'foo' from NAME=foo, only on the line with NAME=foo + DISTRO=$(sed -n -e 's/^NAME="\(.*\)\"/\1/p' /etc/os-release) + else + DISTRO='' + fi + echo $DISTRO +} + case $(uname -s) in #------------------------------------------------------------------------------ @@ -124,7 +129,7 @@ case $(uname -s) in #------------------------------------------------------------------------------ Linux) - case $(lsb_release -is) in + case $(detect_linux_distro) in #------------------------------------------------------------------------------ # Arch Linux @@ -147,15 +152,16 @@ case $(uname -s) in # Alpine Linux #------------------------------------------------------------------------------ - Alpine) + "Alpine Linux") #Alpine echo "Installing solidity dependencies on Alpine Linux." - echo "ERROR - 'install_deps.sh' doesn't have Alpine Linux support yet." - echo "See http://solidity.readthedocs.io/en/latest/installing-solidity.html for manual instructions." - echo "If you would like to get 'install_deps.sh' working for Alpine Linux, that would be fantastic." - echo "Drop us a message at https://gitter.im/ethereum/solidity." - echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support." - exit 1 + + # All our dependencies can be found in the Alpine Linux official repositories. + # See https://pkgs.alpinelinux.org/ + + apk update + apk add boost-dev build-base cmake jsoncpp-dev + ;; #------------------------------------------------------------------------------ diff --git a/scripts/release.bat b/scripts/release.bat index 6f0ef6b0..54d85862 100644 --- a/scripts/release.bat +++ b/scripts/release.bat @@ -28,7 +28,6 @@ REM --------------------------------------------------------------------------- set CONFIGURATION=%1 -REM TODO - Add soltest\%CONFIGURATION%\soltest.exe, when that is buildable. -7z a solidity-develop-windows.zip ^ - .\build\solc\%CONFIGURATION%\solc.exe ^ +7z a solidity-windows.zip ^ + .\build\solc\%CONFIGURATION%\solc.exe .\build\test\%CONFIGURATION%\soltest.exe ^ "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\redist\x86\Microsoft.VC140.CRT\msvc*.dll" diff --git a/scripts/release.sh b/scripts/release.sh index 275e53bb..e9f43f6c 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -89,4 +89,4 @@ fi # And ZIP it all up, with a filename suffix passed in on the command-line. -zip -j $REPO_ROOT/solidity-develop-$ZIP_SUFFIX.zip $ZIP_TEMP_DIR/* +zip -j $REPO_ROOT/solidity-$ZIP_SUFFIX.zip $ZIP_TEMP_DIR/* diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index 68ee7cdc..2fd286fd 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -27,6 +27,7 @@ else fi if [ "$branch" = develop ] +then pparepo=ethereum/ethereum-dev ppafilesurl=https://launchpad.net/~ethereum/+archive/ubuntu/ethereum-dev/+files else @@ -57,7 +58,7 @@ commitdate=`git show --format=%ci HEAD | head -n 1 | cut - -b1-10` # TODO store the commit hash in a file so that the build info mechanism can pick it up even without git -if [ $branch = develop] +if [ $branch = develop ] then debversion="$version-nightly-$commitdate-$commithash" else @@ -67,7 +68,7 @@ fi # gzip will create different tars all the time and we are not allowed # to upload the same file twice with different contents, so we only # create it once. -if [ -n -e /tmp/${packagename}_${debversion}.orig.tar.gz ] +if [ ! -e /tmp/${packagename}_${debversion}.orig.tar.gz ] then tar --exclude .git -czf /tmp/${packagename}_${debversion}.orig.tar.gz . fi @@ -117,6 +118,8 @@ Package: solc Architecture: any-i386 any-amd64 Multi-Arch: same Depends: \${shlibs:Depends}, \${misc:Depends}, $jsoncpplib +Replaces: lllc (<< 1:0.3.6) +Conflicts: libethereum (<= 1.2.9) Description: Solidity compiler. The commandline interface to the Solidity smart contract compiler. EOF @@ -194,7 +197,7 @@ mkdir debian/source echo "3.0 (quilt)" > debian/source/format chmod +x debian/rules -versionsuffix=0ubuntu4~${distribution} +versionsuffix=0ubuntu1~${distribution} # bump version / add entry to changelog EMAIL="$email" dch -v 1:${debversion}-${versionsuffix} "git build of ${commithash}" diff --git a/scripts/tests.sh b/scripts/tests.sh index 5c482d3d..f3bcf65f 100755 --- a/scripts/tests.sh +++ b/scripts/tests.sh @@ -53,7 +53,7 @@ $ETH_PATH --test -d /tmp/test & while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done # And then run the Solidity unit-tests, pointing to that IPC endpoint. -"$REPO_ROOT"/build/test/soltest --ipc /tmp/test/geth.ipc +"$REPO_ROOT"/build/test/soltest -- --ipc /tmp/test/geth.ipc ERROR_CODE=$? pkill eth || true sleep 4 diff --git a/scripts/travis-emscripten/publish_binary.sh b/scripts/travis-emscripten/publish_binary.sh index a62d4b47..ac1fa95f 100755 --- a/scripts/travis-emscripten/publish_binary.sh +++ b/scripts/travis-emscripten/publish_binary.sh @@ -52,19 +52,20 @@ git config user.email "chris@ethereum.org" git checkout -B gh-pages origin/gh-pages git clean -f -d -x # We only want one release per day and we do not want to push the same commit twice. -if ls ./bin/soljson-"$VER-$DATE"-*.js ./bin/soljson-*-"$COMMIT.js" > /dev/null +if ls ./bin/soljson-"$VER-$DATE"-*.js || ls ./bin/soljson-*-"$COMMIT.js" then - true -else - # This file is assumed to be the product of the build_emscripten.sh script. - cp ../soljson.js ./bin/"soljson-$VER-$DATE-$COMMIT.js" - ./update-index.sh - cd bin - LATEST=$(ls -r soljson-v* | head -n 1) - cp "$LATEST" soljson-latest.js - cp soljson-latest.js ../soljson.js - git add . - git add ../soljson.js - git commit -m "Added compiler version $LATEST" - git push origin gh-pages + echo "Not publishing, we already published this version today." + exit 0 fi + +# This file is assumed to be the product of the build_emscripten.sh script. +cp ../soljson.js ./bin/"soljson-$VER-$DATE-$COMMIT.js" +./update-index.sh +cd bin +LATEST=$(ls -r soljson-v* | head -n 1) +cp "$LATEST" soljson-latest.js +cp soljson-latest.js ../soljson.js +git add . +git add ../soljson.js +git commit -m "Added compiler version $LATEST" +git push origin gh-pages diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index ec87b891..08c08797 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -65,7 +65,6 @@ namespace solidity { static string const g_argAbiStr = "abi"; -static string const g_argSolInterfaceStr = "interface"; static string const g_argSignatureHashes = "hashes"; static string const g_argGas = "gas"; static string const g_argAsmStr = "asm"; @@ -116,7 +115,6 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) return false; for (string const& arg: { g_argAbiStr, - g_argSolInterfaceStr, g_argSignatureHashes, g_argNatspecUserStr, g_argAstJson, @@ -215,11 +213,6 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co suffix = ".abi"; title = "Contract JSON ABI"; break; - case DocumentationType::ABISolidityInterface: - argName = g_argSolInterfaceStr; - suffix = "_interface.sol"; - title = "Contract Solidity ABI"; - break; case DocumentationType::NatspecUser: argName = g_argNatspecUserStr; suffix = ".docuser"; @@ -310,21 +303,18 @@ void CommandLineInterface::handleFormal() void CommandLineInterface::readInputFilesAndConfigureRemappings() { + vector<string> inputFiles; + bool addStdin = false; if (!m_args.count("input-file")) - { - string s; - while (!cin.eof()) - { - getline(cin, s); - m_sourceCodes[g_stdinFileName].append(s + '\n'); - } - } + addStdin = true; else for (string path: m_args["input-file"].as<vector<string>>()) { auto eq = find(path.begin(), path.end(), '='); if (eq != path.end()) path = string(eq + 1, path.end()); + else if (path == "-") + addStdin = true; else { auto infile = boost::filesystem::path(path); @@ -345,6 +335,15 @@ void CommandLineInterface::readInputFilesAndConfigureRemappings() } m_allowedDirectories.push_back(boost::filesystem::path(path).remove_filename()); } + if (addStdin) + { + string s; + while (!cin.eof()) + { + getline(cin, s); + m_sourceCodes[g_stdinFileName].append(s + '\n'); + } + } } bool CommandLineInterface::parseLibraryOption(string const& _input) @@ -399,9 +398,9 @@ bool CommandLineInterface::parseArguments(int _argc, char** _argv) po::options_description desc( R"(solc, the Solidity commandline compiler. Usage: solc [options] [input_file...] -Compiles the given Solidity input files (or the standard input if none given) and -outputs the components specified in the options at standard output or in files in -the output directory, if specified. +Compiles the given Solidity input files (or the standard input if none given or +"-" is used as a file name) and outputs the components specified in the options +at standard output or in files in the output directory, if specified. Example: solc --bin -o /tmp/solcoutput contract.sol Allowed options)", @@ -455,7 +454,6 @@ Allowed options)", (g_argRuntimeBinaryStr.c_str(), "Binary of the runtime part of the contracts in hex.") (g_argCloneBinaryStr.c_str(), "Binary of the clone contracts in hex.") (g_argAbiStr.c_str(), "ABI specification of the contracts.") - (g_argSolInterfaceStr.c_str(), "Solidity interface of the contracts.") (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") (g_argNatspecUserStr.c_str(), "Natspec user documentation of all contracts.") (g_argNatspecDevStr.c_str(), "Natspec developer documentation of all contracts.") @@ -643,8 +641,6 @@ void CommandLineInterface::handleCombinedJSON() for (string const& contractName: contracts) { Json::Value contractData(Json::objectValue); - if (requests.count("interface")) - contractData["interface"] = m_compiler->solidityInterface(contractName); if (requests.count("abi")) contractData["abi"] = m_compiler->interface(contractName); if (requests.count("bin")) @@ -901,7 +897,6 @@ void CommandLineInterface::outputCompilationResults() handleBytecode(contract); handleSignatureHashes(contract); handleMeta(DocumentationType::ABIInterface, contract); - handleMeta(DocumentationType::ABISolidityInterface, contract); handleMeta(DocumentationType::NatspecDev, contract); handleMeta(DocumentationType::NatspecUser, contract); } // end of contracts iteration diff --git a/solc/jsonCompiler.cpp b/solc/jsonCompiler.cpp index 20112103..896a5922 100644 --- a/solc/jsonCompiler.cpp +++ b/solc/jsonCompiler.cpp @@ -207,7 +207,6 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback for (string const& contractName: compiler.contractNames()) { Json::Value contractData(Json::objectValue); - contractData["solidity_interface"] = compiler.solidityInterface(contractName); contractData["interface"] = compiler.interface(contractName); contractData["bytecode"] = compiler.object(contractName).toHex(); contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex(); @@ -217,7 +216,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback auto sourceMap = compiler.sourceMapping(contractName); contractData["srcmap"] = sourceMap ? *sourceMap : ""; auto runtimeSourceMap = compiler.runtimeSourceMapping(contractName); - contractData["srcmap-runtime"] = runtimeSourceMap ? *runtimeSourceMap : ""; + contractData["srcmapRuntime"] = runtimeSourceMap ? *runtimeSourceMap : ""; ostringstream unused; contractData["assembly"] = compiler.streamAssembly(unused, contractName, _sources, true); output["contracts"][contractName] = contractData; diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 8ed9e9ac..de10b381 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -244,15 +244,38 @@ void RPCSession::test_mineBlocks(int _number) u256 startBlock = fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())); rpcCall("test_mineBlocks", { to_string(_number) }, true); - //@TODO do not use polling - but that would probably need a change to the test client - for (size_t polls = 0; polls < 100; ++polls) + bool mined = false; + + // We auto-calibrate the time it takes to mine the transaction. + // It would be better to go without polling, but that would probably need a change to the test client + + unsigned sleepTime = m_sleepTime; + size_t polls = 0; + for (; polls < 10 && !mined; ++polls) { + std::this_thread::sleep_for(chrono::milliseconds(sleepTime)); if (fromBigEndian<u256>(fromHex(rpcCall("eth_blockNumber").asString())) >= startBlock + _number) - return; - std::this_thread::sleep_for(chrono::milliseconds(10)); //it does not work faster then 10 ms + mined = true; + else + sleepTime *= 2; + } + if (polls > 1) + { + m_successfulMineRuns = 0; + m_sleepTime += 2; + } + else if (polls == 1) + { + m_successfulMineRuns++; + if (m_successfulMineRuns > 5) + { + m_successfulMineRuns = 0; + m_sleepTime--; + } } - BOOST_FAIL("Error in test_mineBlocks: block mining timeout!"); + if (!mined) + BOOST_FAIL("Error in test_mineBlocks: block mining timeout!"); } void RPCSession::test_modifyTimestamp(size_t _timestamp) diff --git a/test/RPCSession.h b/test/RPCSession.h index 9b7009bf..2a9825b0 100644 --- a/test/RPCSession.h +++ b/test/RPCSession.h @@ -124,6 +124,8 @@ private: IPCSocket m_ipcSocket; size_t m_rpcSequence = 1; + unsigned m_sleepTime = 10; + unsigned m_successfulMineRuns = 0; std::vector<std::string> m_accounts; }; diff --git a/test/TestHelper.cpp b/test/TestHelper.cpp index a704ab17..bfc5b54c 100644 --- a/test/TestHelper.cpp +++ b/test/TestHelper.cpp @@ -24,28 +24,22 @@ using namespace std; using namespace dev::test; -Options::Options(int argc, char** argv) +Options const& Options::get() { - tArgc = 0; - tArgv = new char*[argc]; - for (auto i = 0; i < argc; i++) - { - string arg = argv[i]; - if (arg == "--ipc" && i + 1 < argc) + static Options instance; + return instance; +} + +Options::Options() +{ + auto const& suite = boost::unit_test::framework::master_test_suite(); + for (auto i = 0; i < suite.argc; i++) + if (string(suite.argv[i]) == "--ipc" && i + 1 < suite.argc) { - ipcPath = argv[i + 1]; + ipcPath = suite.argv[i + 1]; i++; } - else - { - tArgv[i] = argv[i]; - tArgc++; - } - } + if (ipcPath.empty()) + if (auto path = getenv("ETH_TEST_IPC")) + ipcPath = path; } - -Options const& Options::get(int argc, char** argv) -{ - static Options instance(argc, argv); - return instance; -}
\ No newline at end of file diff --git a/test/TestHelper.h b/test/TestHelper.h index 49931614..2cb24fd7 100644 --- a/test/TestHelper.h +++ b/test/TestHelper.h @@ -103,19 +103,15 @@ namespace test while (0) - class Options - { - public: - std::string ipcPath; - int tArgc; - char **tArgv; - /// Get reference to options - /// The first time used, options are parsed with argc, argv - static Options const& get(int argc = 0, char** argv = 0); +struct Options: boost::noncopyable +{ + std::string ipcPath; + + static Options const& get(); + +private: + Options(); +}; - private: - Options(int argc, char** argv = 0); - Options(Options const&) = delete; - }; } } diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 7ed2a6cd..4ddae0b7 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -26,59 +26,13 @@ #pragma GCC diagnostic ignored "-Wunused-parameter" - #define BOOST_TEST_NO_MAIN - #if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4535) // calling _set_se_translator requires /EHa - #endif - #include <boost/test/included/unit_test.hpp> - #if defined(_MSC_VER) - #pragma warning(pop) - #endif - - #pragma GCC diagnostic pop - - #include <stdlib.h> - #include <boost/version.hpp> - #include "TestHelper.h" - - using namespace boost::unit_test; - - std::vector<char*> parameters; - static std::ostringstream strCout; - std::streambuf* oldCoutStreamBuf; - std::streambuf* oldCerrStreamBuf; - - //Custom Boost Initialization - test_suite* fake_init_func(int argc, char* argv[]) - { - //Required for boost. -nowarning - (void)argc; - (void)argv; - return 0; - } - - //Custom Boost Unit Test Main - int main(int argc, char* argv[]) - { - //Initialize options before boost reads it - dev::test::Options const& opt = dev::test::Options::get(argc, argv); - return unit_test_main(fake_init_func, opt.tArgc, opt.tArgv); - } - - /* -#else - #if defined(_MSC_VER) - #pragma warning(push) - #pragma warning(disable:4535) // calling _set_se_translator requires /EHa - #endif - #include <boost/test/included/unit_test.hpp> - #if defined(_MSC_VER) - #pragma warning(pop) - #endif - - #pragma GCC diagnostic pop - - #include <test/TestHelper.h> - using namespace boost::unit_test; -#endif*/
\ No newline at end of file +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4535) // calling _set_se_translator requires /EHa +#endif +#include <boost/test/included/unit_test.hpp> +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + +#pragma GCC diagnostic pop diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index 6d914391..ec60b668 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -45,7 +45,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); - BOOST_CHECK_EQUAL(astJson["name"], "root"); + BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); } BOOST_AUTO_TEST_CASE(source_location) @@ -56,12 +56,145 @@ BOOST_AUTO_TEST_CASE(source_location) map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); - BOOST_CHECK_EQUAL(astJson["name"], "root"); - BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "Contract"); - BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "Function"); + BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); + BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition"); + BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition"); BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["src"], "13:32:1"); } +BOOST_AUTO_TEST_CASE(inheritance_specifier) +{ + CompilerStack c; + c.addSource("a", "contract C1 {} contract C2 is C1 {}"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["name"], "UserDefinedTypeName"); + BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["children"][0]["attributes"]["name"], "C1"); +} + +BOOST_AUTO_TEST_CASE(using_for_directive) +{ + CompilerStack c; + c.addSource("a", "library L {} contract C { using L for uint; }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value usingFor = astJson["children"][1]["children"][0]; + BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective"); + BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1"); + BOOST_CHECK_EQUAL(usingFor["children"][0]["name"], "UserDefinedTypeName"); + BOOST_CHECK_EQUAL(usingFor["children"][0]["attributes"]["name"], "L"); + BOOST_CHECK_EQUAL(usingFor["children"][1]["name"], "ElementaryTypeName"); + BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint"); +} + +BOOST_AUTO_TEST_CASE(enum_definition) +{ + CompilerStack c; + c.addSource("a", "contract C { enum E {} }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value enumDefinition = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(enumDefinition["name"], "EnumDefinition"); + BOOST_CHECK_EQUAL(enumDefinition["attributes"]["name"], "E"); + BOOST_CHECK_EQUAL(enumDefinition["src"], "13:9:1"); +} + +BOOST_AUTO_TEST_CASE(enum_value) +{ + CompilerStack c; + c.addSource("a", "contract C { enum E { A, B } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value enumDefinition = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue"); + BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A"); + BOOST_CHECK_EQUAL(enumDefinition["children"][0]["src"], "22:1:1"); + BOOST_CHECK_EQUAL(enumDefinition["children"][1]["name"], "EnumValue"); + BOOST_CHECK_EQUAL(enumDefinition["children"][1]["attributes"]["name"], "B"); + BOOST_CHECK_EQUAL(enumDefinition["children"][1]["src"], "25:1:1"); +} + +BOOST_AUTO_TEST_CASE(modifier_definition) +{ + CompilerStack c; + c.addSource("a", "contract C { modifier M(uint i) { _ } function F() M(1) {} }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value modifier = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition"); + BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M"); + BOOST_CHECK_EQUAL(modifier["src"], "13:24:1"); +} + +BOOST_AUTO_TEST_CASE(modifier_invocation) +{ + CompilerStack c; + c.addSource("a", "contract C { modifier M(uint i) { _ } function F() M(1) {} }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value modifier = astJson["children"][0]["children"][1]["children"][2]; + BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation"); + BOOST_CHECK_EQUAL(modifier["src"], "51:4:1"); + BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["type"], "modifier (uint256)"); + BOOST_CHECK_EQUAL(modifier["children"][0]["attributes"]["value"], "M"); + BOOST_CHECK_EQUAL(modifier["children"][1]["attributes"]["value"], "1"); +} + +BOOST_AUTO_TEST_CASE(event_definition) +{ + CompilerStack c; + c.addSource("a", "contract C { event E(); }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value event = astJson["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(event["name"], "EventDefinition"); + BOOST_CHECK_EQUAL(event["attributes"]["name"], "E"); + BOOST_CHECK_EQUAL(event["src"], "13:10:1"); +} + +BOOST_AUTO_TEST_CASE(array_type_name) +{ + CompilerStack c; + c.addSource("a", "contract C { uint[] i; }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value array = astJson["children"][0]["children"][0]["children"][0]; + BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName"); + BOOST_CHECK_EQUAL(array["src"], "13:6:1"); +} + +BOOST_AUTO_TEST_CASE(placeholder_statement) +{ + CompilerStack c; + c.addSource("a", "contract C { modifier M { _ } }"); + c.parse(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 1; + Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0]; + BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement"); + BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1"); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 1b7c5ea4..4e6f68b0 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2390,7 +2390,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_invocation) BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return) { - // Here, the explicit return prevents the second execution + // Note that return sets the return variable and jumps to the end of the current function or + // modifier code block. char const* sourceCode = R"( contract C { modifier repeat(bool twice) { if (twice) _ _ } @@ -2399,7 +2400,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_multi_with_return) )"; compileAndRun(sourceCode); BOOST_CHECK(callContractFunction("f(bool)", false) == encodeArgs(1)); - BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(1)); + BOOST_CHECK(callContractFunction("f(bool)", true) == encodeArgs(2)); } BOOST_AUTO_TEST_CASE(function_modifier_overriding) @@ -2410,7 +2411,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_overriding) modifier mod { _ } } contract C is A { - modifier mod { } + modifier mod { if (false) _ } } )"; compileAndRun(sourceCode); @@ -2427,7 +2428,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_calling_functions_in_creation_context) function f2() { data |= 0x20; } function f3() { } modifier mod1 { f2(); _ } - modifier mod2 { f3(); } + modifier mod2 { f3(); if (false) _ } function getData() returns (uint r) { return data; } } contract C is A { @@ -6208,6 +6209,27 @@ BOOST_AUTO_TEST_CASE(addmod_mulmod) BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(0))); } +BOOST_AUTO_TEST_CASE(divisiod_by_zero) +{ + char const* sourceCode = R"( + contract C { + function div(uint a, uint b) returns (uint) { + return a / b; + } + function mod(uint a, uint b) returns (uint) { + return a % b; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("div(uint256,uint256)", 7, 2) == encodeArgs(u256(3))); + // throws + BOOST_CHECK(callContractFunction("div(uint256,uint256)", 7, 0) == encodeArgs()); + BOOST_CHECK(callContractFunction("mod(uint256,uint256)", 7, 2) == encodeArgs(u256(1))); + // throws + BOOST_CHECK(callContractFunction("mod(uint256,uint256)", 7, 0) == encodeArgs()); +} + BOOST_AUTO_TEST_CASE(string_allocation_bug) { char const* sourceCode = R"( @@ -6839,6 +6861,33 @@ BOOST_AUTO_TEST_CASE(skip_dynamic_types_for_structs) BOOST_CHECK(callContractFunction("g()") == encodeArgs(u256(2), u256(6))); } +BOOST_AUTO_TEST_CASE(failed_create) +{ + char const* sourceCode = R"( + contract D { } + contract C { + uint public x; + function f(uint amount) returns (address) { + x++; + return (new D).value(amount)(); + } + function stack(uint depth) returns (address) { + if (depth < 1024) + return this.stack(depth - 1); + else + return f(0); + } + } + )"; + compileAndRun(sourceCode, 20, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", 20) != encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256)", 20) == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("stack(uint256)", 1023) == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); +} + BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) { char const* sourceCode = R"( @@ -6853,6 +6902,179 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7))); } +BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier setsx { + _ + x = 9; + } + function f() setsx returns (uint) { + return 2; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(9))); +} + +BOOST_AUTO_TEST_CASE(break_in_modifier) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier run() { + for (uint i = 0; i < 10; i++) { + _ + break; + } + } + function f() run { + x++; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(stacked_return_with_modifiers) +{ + char const* sourceCode = R"( + contract C { + uint public x; + modifier run() { + for (uint i = 0; i < 10; i++) { + _ + break; + } + } + function f() run { + x++; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1))); +} + +BOOST_AUTO_TEST_CASE(mutex) +{ + char const* sourceCode = R"( + contract mutexed { + bool locked; + modifier protected { + if (locked) throw; + locked = true; + _ + locked = false; + } + } + contract Fund is mutexed { + uint shares; + function Fund() { shares = msg.value; } + function withdraw(uint amount) protected returns (uint) { + // NOTE: It is very bad practice to write this function this way. + // Please refer to the documentation of how to do this properly. + if (amount > shares) throw; + if (!msg.sender.call.value(amount)()) throw; + shares -= amount; + return shares; + } + function withdrawUnprotected(uint amount) returns (uint) { + // NOTE: It is very bad practice to write this function this way. + // Please refer to the documentation of how to do this properly. + if (amount > shares) throw; + if (!msg.sender.call.value(amount)()) throw; + shares -= amount; + return shares; + } + } + contract Attacker { + Fund public fund; + uint callDepth; + bool protected; + function setProtected(bool _protected) { protected = _protected; } + function Attacker(Fund _fund) { fund = _fund; } + function attack() returns (uint) { + callDepth = 0; + return attackInternal(); + } + function attackInternal() internal returns (uint) { + if (protected) + return fund.withdraw(10); + else + return fund.withdrawUnprotected(10); + } + function() { + callDepth++; + if (callDepth < 4) + attackInternal(); + } + } + )"; + compileAndRun(sourceCode, 500, "Fund"); + auto fund = m_contractAddress; + BOOST_CHECK_EQUAL(balanceAt(fund), 500); + compileAndRun(sourceCode, 0, "Attacker", encodeArgs(u160(fund))); + BOOST_CHECK(callContractFunction("setProtected(bool)", true) == encodeArgs()); + BOOST_CHECK(callContractFunction("attack()") == encodeArgs()); + BOOST_CHECK_EQUAL(balanceAt(fund), 500); + BOOST_CHECK(callContractFunction("setProtected(bool)", false) == encodeArgs()); + BOOST_CHECK(callContractFunction("attack()") == encodeArgs(u256(460))); + BOOST_CHECK_EQUAL(balanceAt(fund), 460); +} + +BOOST_AUTO_TEST_CASE(failing_ecrecover_invalid_input) +{ + // ecrecover should return zero for malformed input + // (v should be 27 or 28, not 1) + // Note that the precompile does not return zero but returns nothing. + char const* sourceCode = R"( + contract C { + function f() returns (address) { + return ecrecover(bytes32(uint(-1)), 1, 2, 3); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0))); +} + +BOOST_AUTO_TEST_CASE(calling_nonexisting_contract_throws) +{ + char const* sourceCode = R"( + contract D { function g(); } + contract C { + D d = D(0x1212); + function f() returns (uint) { + d.g(); + return 7; + } + function g() returns (uint) { + d.g.gas(200)(); + return 7; + } + function h() returns (uint) { + d.call(); // this does not throw (low-level) + return 7; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs()); + BOOST_CHECK(callContractFunction("g()") == encodeArgs()); + BOOST_CHECK(callContractFunction("h()") == encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExecutionFramework.cpp b/test/libsolidity/SolidityExecutionFramework.cpp index 921fd056..d0ee13c6 100644 --- a/test/libsolidity/SolidityExecutionFramework.cpp +++ b/test/libsolidity/SolidityExecutionFramework.cpp @@ -39,9 +39,6 @@ string getIPCSocketPath() { string ipcPath = dev::test::Options::get().ipcPath; if (ipcPath.empty()) - if (auto path = getenv("ETH_TEST_IPC")) - ipcPath = path; - if (ipcPath.empty()) BOOST_FAIL("ERROR: ipcPath not set! (use --ipc <path> or the environment variable ETH_TEST_IPC)"); return ipcPath; diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index 5764784a..c34b00ec 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -33,6 +33,7 @@ #include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/SourceReferenceFormatter.h> namespace dev { @@ -68,7 +69,17 @@ public: { m_compiler.reset(false, m_addStandardSources); m_compiler.addSource("", _sourceCode); - ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); + if (!m_compiler.compile(m_optimize, m_optimizeRuns)) + { + for (auto const& error: m_compiler.errors()) + SourceReferenceFormatter::printExceptionInformation( + std::cerr, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const& _sourceName) -> solidity::Scanner const& { return m_compiler.scanner(_sourceName); } + ); + BOOST_ERROR("Compiling contract failed"); + } eth::LinkerObject obj = m_compiler.object(_contractName); obj.link(_libraryAddresses); BOOST_REQUIRE(obj.linkReferences.empty()); diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 967b2907..e9a05745 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -323,7 +323,15 @@ BOOST_AUTO_TEST_CASE(arithmetics) byte(Instruction::OR), byte(Instruction::SUB), byte(Instruction::ADD), + byte(Instruction::DUP2), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x2, + byte(Instruction::JUMPI), byte(Instruction::MOD), + byte(Instruction::DUP2), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), 0x2, + byte(Instruction::JUMPI), byte(Instruction::DIV), byte(Instruction::MUL)}); BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end()); diff --git a/test/libsolidity/SolidityInterface.cpp b/test/libsolidity/SolidityInterface.cpp deleted file mode 100644 index 9a1c104d..00000000 --- a/test/libsolidity/SolidityInterface.cpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - This file is part of cpp-ethereum. - - cpp-ethereum is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - cpp-ethereum is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. - */ -/** - * @author Christian <c@ethdev.com> - * @date 2015 - * Unit tests for generating source interfaces for Solidity contracts. - */ - -#include "../TestHelper.h" -#include <libsolidity/interface/CompilerStack.h> -#include <libsolidity/ast/AST.h> - -using namespace std; - -namespace dev -{ -namespace solidity -{ -namespace test -{ - -class SolidityInterfaceChecker -{ -public: - SolidityInterfaceChecker(): m_compilerStack(false) {} - - /// Compiles the given code, generates the interface and parses that again. - ContractDefinition const& checkInterface(string const& _code, string const& _contractName = "") - { - m_code = _code; - ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse(_code), "Parsing failed"); - m_interface = m_compilerStack.metadata("", DocumentationType::ABISolidityInterface); - ETH_TEST_REQUIRE_NO_THROW(m_reCompiler.parse(m_interface), "Interface parsing failed"); - return m_reCompiler.contractDefinition(_contractName); - } - - string sourcePart(ASTNode const& _node) const - { - SourceLocation location = _node.location(); - BOOST_REQUIRE(!location.isEmpty()); - return m_interface.substr(location.start, location.end - location.start); - } - -protected: - string m_code; - string m_interface; - CompilerStack m_compilerStack; - CompilerStack m_reCompiler; -}; - -BOOST_FIXTURE_TEST_SUITE(SolidityInterface, SolidityInterfaceChecker) - -BOOST_AUTO_TEST_CASE(empty_contract) -{ - ContractDefinition const& contract = checkInterface("contract test {}"); - BOOST_CHECK_EQUAL(sourcePart(contract), "contract test{}"); -} - -BOOST_AUTO_TEST_CASE(single_function) -{ - ContractDefinition const& contract = checkInterface( - "contract test {\n" - " function f(uint a) returns(uint d) { return a * 7; }\n" - "}\n"); - BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size()); - BOOST_CHECK_EQUAL(sourcePart(*contract.definedFunctions().front()), - "function f(uint256 a)returns(uint256 d);"); -} - -BOOST_AUTO_TEST_CASE(single_constant_function) -{ - ContractDefinition const& contract = checkInterface( - "contract test { function f(uint a) constant returns(bytes1 x) { 1==2; } }"); - BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size()); - BOOST_CHECK_EQUAL(sourcePart(*contract.definedFunctions().front()), - "function f(uint256 a)constant returns(bytes1 x);"); -} - -BOOST_AUTO_TEST_CASE(multiple_functions) -{ - char const* sourceCode = "contract test {\n" - " function f(uint a) returns(uint d) { return a * 7; }\n" - " function g(uint b) returns(uint e) { return b * 8; }\n" - "}\n"; - ContractDefinition const& contract = checkInterface(sourceCode); - set<string> expectation({"function f(uint256 a)returns(uint256 d);", - "function g(uint256 b)returns(uint256 e);"}); - BOOST_REQUIRE_EQUAL(2, contract.definedFunctions().size()); - BOOST_CHECK(expectation == set<string>({sourcePart(*contract.definedFunctions().at(0)), - sourcePart(*contract.definedFunctions().at(1))})); -} - -BOOST_AUTO_TEST_CASE(exclude_fallback_function) -{ - char const* sourceCode = "contract test { function() {} }"; - ContractDefinition const& contract = checkInterface(sourceCode); - BOOST_CHECK_EQUAL(sourcePart(contract), "contract test{}"); -} - -BOOST_AUTO_TEST_CASE(events) -{ - char const* sourceCode = "contract test {\n" - " function f(uint a) returns(uint d) { return a * 7; }\n" - " event e1(uint b, address indexed c); \n" - " event e2(); \n" - "}\n"; - ContractDefinition const& contract = checkInterface(sourceCode); - // events should not appear in the Solidity Interface - BOOST_REQUIRE_EQUAL(0, contract.events().size()); -} - -BOOST_AUTO_TEST_CASE(inheritance) -{ - char const* sourceCode = - " contract Base { \n" - " function baseFunction(uint p) returns (uint i) { return p; } \n" - " event baseEvent(bytes32 indexed evtArgBase); \n" - " } \n" - " contract Derived is Base { \n" - " function derivedFunction(bytes32 p) returns (bytes32 i) { return p; } \n" - " event derivedEvent(uint indexed evtArgDerived); \n" - " }"; - ContractDefinition const& contract = checkInterface(sourceCode); - set<string> expectedFunctions({"function baseFunction(uint256 p)returns(uint256 i);", - "function derivedFunction(bytes32 p)returns(bytes32 i);"}); - BOOST_REQUIRE_EQUAL(2, contract.definedFunctions().size()); - BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0)), - sourcePart(*contract.definedFunctions().at(1))})); -} - -BOOST_AUTO_TEST_CASE(libraries) -{ - char const* sourceCode = R"( - library Lib { - struct Str { uint a; } - enum E { E1, E2 } - function f(uint[] x,Str storage y,E z) external; - } - )"; - ContractDefinition const& contract = checkInterface(sourceCode); - BOOST_CHECK(contract.isLibrary()); - set<string> expectedFunctions({"function f(uint256[] x,Lib.Str storage y,Lib.E z);"}); - BOOST_REQUIRE_EQUAL(1, contract.definedFunctions().size()); - BOOST_CHECK(expectedFunctions == set<string>({sourcePart(*contract.definedFunctions().at(0))})); -} - -BOOST_AUTO_TEST_SUITE_END() - -} -} -} diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 7e81bd7e..e9da390c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -901,8 +901,8 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) BOOST_AUTO_TEST_CASE(legal_modifier_override) { char const* text = R"( - contract A { modifier mod(uint a) {} } - contract B is A { modifier mod(uint a) {} } + contract A { modifier mod(uint a) { _ } } + contract B is A { modifier mod(uint a) { _ } } )"; BOOST_CHECK(success(text)); } @@ -910,8 +910,8 @@ BOOST_AUTO_TEST_CASE(legal_modifier_override) BOOST_AUTO_TEST_CASE(illegal_modifier_override) { char const* text = R"( - contract A { modifier mod(uint a) {} } - contract B is A { modifier mod(uint8 a) {} } + contract A { modifier mod(uint a) { _ } } + contract B is A { modifier mod(uint8 a) { _ } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); } @@ -919,8 +919,8 @@ BOOST_AUTO_TEST_CASE(illegal_modifier_override) BOOST_AUTO_TEST_CASE(modifier_overrides_function) { char const* text = R"( - contract A { modifier mod(uint a) {} } - contract B is A { function mod(uint a) {} } + contract A { modifier mod(uint a) { _ } } + contract B is A { function mod(uint a) { } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); } @@ -928,8 +928,8 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) BOOST_AUTO_TEST_CASE(function_overrides_modifier) { char const* text = R"( - contract A { function mod(uint a) {} } - contract B is A { modifier mod(uint a) {} } + contract A { function mod(uint a) { } } + contract B is A { modifier mod(uint a) { _ } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); } @@ -938,8 +938,8 @@ BOOST_AUTO_TEST_CASE(modifier_returns_value) { char const* text = R"( contract A { - function f(uint a) mod(2) returns (uint r) {} - modifier mod(uint a) { return 7; } + function f(uint a) mod(2) returns (uint r) { } + modifier mod(uint a) { _ return 7; } } )"; BOOST_CHECK(expectError(text) == Error::Type::TypeError); @@ -3823,6 +3823,16 @@ BOOST_AUTO_TEST_CASE(unused_return_value_delegatecall) BOOST_CHECK(expectError(text, true) == Error::Type::Warning); } +BOOST_AUTO_TEST_CASE(modifier_without_underscore) +{ + char const* text = R"( + contract test { + modifier m() {} + } + )"; + BOOST_CHECK(expectError(text, true) == Error::Type::SyntaxError); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityScanner.cpp b/test/libsolidity/SolidityScanner.cpp index 4443b9f6..31b75f25 100644 --- a/test/libsolidity/SolidityScanner.cpp +++ b/test/libsolidity/SolidityScanner.cpp @@ -275,12 +275,6 @@ BOOST_AUTO_TEST_CASE(time_subdenominations) BOOST_CHECK_EQUAL(scanner.next(), Token::SubYear); } -BOOST_AUTO_TEST_CASE(time_after) -{ - Scanner scanner(CharStream("after 1")); - BOOST_CHECK_EQUAL(scanner.currentToken(), Token::After); -} - BOOST_AUTO_TEST_CASE(empty_comment) { Scanner scanner(CharStream("//\ncontract{}")); @@ -330,6 +324,42 @@ BOOST_AUTO_TEST_CASE(invalid_short_unicode_string_escape) BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); } +BOOST_AUTO_TEST_CASE(valid_hex_literal) +{ + Scanner scanner(CharStream("{ hex\"00112233FF\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::StringLiteral); + BOOST_CHECK_EQUAL(scanner.currentLiteral(), std::string("\x00\x11\x22\x33\xFF", 5)); +} + +BOOST_AUTO_TEST_CASE(invalid_short_hex_literal) +{ + Scanner scanner(CharStream("{ hex\"00112233F\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_space) +{ + Scanner scanner(CharStream("{ hex\"00112233FF \"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_with_wrong_quotes) +{ + Scanner scanner(CharStream("{ hex\"00112233FF'")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + +BOOST_AUTO_TEST_CASE(invalid_hex_literal_nonhex_string) +{ + Scanner scanner(CharStream("{ hex\"hello\"")); + BOOST_CHECK_EQUAL(scanner.currentToken(), Token::LBrace); + BOOST_CHECK_EQUAL(scanner.next(), Token::Illegal); +} + BOOST_AUTO_TEST_SUITE_END() |