mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #3503 from ethereum/develop
Merge develop into release for v0.4.20.
This commit is contained in:
commit
3155dd8058
@ -97,12 +97,12 @@ matrix:
|
||||
sudo: required
|
||||
compiler: gcc
|
||||
node_js:
|
||||
- "6"
|
||||
- "7"
|
||||
services:
|
||||
- docker
|
||||
before_install:
|
||||
- nvm install 6
|
||||
- nvm use 6
|
||||
- nvm install 7
|
||||
- nvm use 7
|
||||
- docker pull trzeci/emscripten:sdk-tag-1.35.4-64bit
|
||||
env:
|
||||
- SOLC_EMSCRIPTEN=On
|
||||
|
@ -8,10 +8,12 @@ include(EthPolicy)
|
||||
eth_policy()
|
||||
|
||||
# project name and version should be set after cmake_policy CMP0048
|
||||
set(PROJECT_VERSION "0.4.19")
|
||||
set(PROJECT_VERSION "0.4.20")
|
||||
project(solidity VERSION ${PROJECT_VERSION})
|
||||
|
||||
option(SOLC_LINK_STATIC "Link solc executable statically on supported platforms" OFF)
|
||||
option(LLLC_LINK_STATIC "Link lllc executable statically on supported platforms" OFF)
|
||||
option(INSTALL_LLLC "Include lllc executable in installation" OFF)
|
||||
|
||||
# Setup cccache.
|
||||
include(EthCcache)
|
||||
@ -43,9 +45,10 @@ configure_project(TESTS)
|
||||
add_subdirectory(libdevcore)
|
||||
add_subdirectory(libevmasm)
|
||||
add_subdirectory(libsolidity)
|
||||
add_subdirectory(solc)
|
||||
add_subdirectory(libsolc)
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
add_subdirectory(solc)
|
||||
add_subdirectory(liblll)
|
||||
add_subdirectory(lllc)
|
||||
endif()
|
||||
|
28
Changelog.md
28
Changelog.md
@ -1,3 +1,31 @@
|
||||
### 0.4.20 (2018-02-14)
|
||||
|
||||
Features:
|
||||
* Code Generator: Prevent non-view functions in libraries from being called
|
||||
directly (as opposed to via delegatecall).
|
||||
* Commandline interface: Support strict mode of assembly (disallowing jumps,
|
||||
instructional opcodes, etc) with the ``--strict-assembly`` switch.
|
||||
* Inline Assembly: Issue warning for using jump labels (already existed for jump instructions).
|
||||
* Inline Assembly: Support some restricted tokens (return, byte, address) as identifiers in Iulia mode.
|
||||
* Optimiser: Replace ``x % 2**i`` by ``x & (2**i-1)``.
|
||||
* Resolver: Continue resolving references after the first error.
|
||||
* Resolver: Suggest alternative identifiers if a given identifier is not found.
|
||||
* SMT Checker: Take if-else branch conditions into account in the SMT encoding of the program
|
||||
variables.
|
||||
* Syntax Checker: Deprecate the ``var`` keyword (and mark it an error as experimental 0.5.0 feature).
|
||||
* Type Checker: Allow `this.f.selector` to be a pure expression.
|
||||
* Type Checker: Issue warning for using ``public`` visibility for interface functions.
|
||||
* Type Checker: Limit the number of warnings raised for creating abstract contracts.
|
||||
|
||||
Bugfixes:
|
||||
* Error Output: Truncate huge number literals in the middle to avoid output blow-up.
|
||||
* Parser: Disallow event declarations with no parameter list.
|
||||
* Standard JSON: Populate the ``sourceLocation`` field in the error list.
|
||||
* Standard JSON: Properly support contract and library file names containing a colon (such as URLs).
|
||||
* Type Checker: Suggest the experimental ABI encoder if using ``struct``s as function parameters
|
||||
(instead of an internal compiler error).
|
||||
* Type Checker: Improve error message for wrong struct initialization.
|
||||
|
||||
### 0.4.19 (2017-11-30)
|
||||
|
||||
Features:
|
||||
|
134
circle.yml
134
circle.yml
@ -1,25 +1,10 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
build_emscripten:
|
||||
docker:
|
||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install external tests deps
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install netcat curl
|
||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
|
||||
- run:
|
||||
name: Test external tests deps
|
||||
command: |
|
||||
export NVM_DIR="/usr/local/nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
nvm --version
|
||||
nvm install 6
|
||||
node --version
|
||||
npm --version
|
||||
- run:
|
||||
name: Init submodules
|
||||
command: |
|
||||
@ -40,11 +25,120 @@ jobs:
|
||||
key: *boost-cache-key
|
||||
paths:
|
||||
- boost_1_57_0
|
||||
- store_artifacts:
|
||||
path: build/libsolc/soljson.js
|
||||
destination: soljson.js
|
||||
- run: mkdir -p workspace
|
||||
- run: cp build/libsolc/soljson.js workspace/soljson.js
|
||||
- run: scripts/get_version.sh > workspace/version.txt
|
||||
- persist_to_workspace:
|
||||
root: workspace
|
||||
paths:
|
||||
- soljson.js
|
||||
- version.txt
|
||||
test_emscripten_solcjs:
|
||||
docker:
|
||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Test
|
||||
name: Install external tests deps
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install netcat curl
|
||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
|
||||
export NVM_DIR="/usr/local/nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
nvm --version
|
||||
nvm install 6
|
||||
node --version
|
||||
npm --version
|
||||
- run:
|
||||
name: Test solcjs
|
||||
command: |
|
||||
. /usr/local/nvm/nvm.sh
|
||||
scripts/test_emscripten.sh
|
||||
test/solcjsTests.sh /tmp/workspace/soljson.js $(cat /tmp/workspace/version.txt)
|
||||
test_emscripten_external:
|
||||
docker:
|
||||
- image: trzeci/emscripten:sdk-tag-1.37.21-64bit
|
||||
steps:
|
||||
- checkout
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- run:
|
||||
name: Install external tests deps
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install netcat curl
|
||||
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.4/install.sh | NVM_DIR=/usr/local/nvm bash
|
||||
export NVM_DIR="/usr/local/nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
nvm --version
|
||||
nvm install 7
|
||||
node --version
|
||||
npm --version
|
||||
- run:
|
||||
name: External tests
|
||||
command: |
|
||||
. /usr/local/nvm/nvm.sh
|
||||
test/externalTests.sh /tmp/workspace/soljson.js
|
||||
build_x86:
|
||||
docker:
|
||||
- image: buildpack-deps:artful
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install build dependencies
|
||||
command: |
|
||||
apt-get -qq update
|
||||
apt-get -qy install ccache cmake libboost-all-dev libz3-dev
|
||||
- run:
|
||||
name: Init submodules
|
||||
command: |
|
||||
git submodule update --init
|
||||
- run:
|
||||
name: Store commit hash and prerelease
|
||||
command: |
|
||||
date -u +"nightly.%Y.%-m.%-d" > prerelease.txt
|
||||
echo -n "$CIRCLE_SHA1" > commit_hash.txt
|
||||
- restore_cache:
|
||||
key: ccache-{{ arch }}-{{ .Branch }}
|
||||
key: ccache-{{ arch }}
|
||||
key: ccache
|
||||
- run:
|
||||
name: Build
|
||||
command: ./scripts/build.sh RelWithDebInfo
|
||||
- save_cache:
|
||||
key: ccache-{{ arch }}-{{ .Branch }}
|
||||
paths:
|
||||
- ~/.ccache
|
||||
- run:
|
||||
name: Commandline tests
|
||||
command: test/cmdlineTests.sh
|
||||
- run: mkdir -p test_results
|
||||
- run:
|
||||
name: Test without optimizer (exclude IPC tests)
|
||||
command: build/test/soltest --logger=JUNIT,test_suite,test_results/no_opt.xml -- --no-ipc
|
||||
- run:
|
||||
name: Test with optimizer (exclude IPC tests)
|
||||
command: build/test/soltest --logger=JUNIT,test_suite,test_results/opt.xml -- --optimize --no-ipc
|
||||
- store_test_results:
|
||||
path: test_results/
|
||||
- store_artifacts:
|
||||
path: build/solc/soljson.js
|
||||
destination: soljson.js
|
||||
path: build/solc/solc
|
||||
destination: solc
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build_all:
|
||||
jobs:
|
||||
- build_emscripten
|
||||
- test_emscripten_solcjs:
|
||||
requires:
|
||||
- build_emscripten
|
||||
- test_emscripten_external:
|
||||
requires:
|
||||
- build_emscripten
|
||||
- build_x86
|
||||
|
@ -94,6 +94,11 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
add_compile_options(-Wno-unused-function)
|
||||
add_compile_options(-Wno-dangling-else)
|
||||
|
||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin")
|
||||
# Set stack size to 16MB - by default Apple's clang defines a stack size of 8MB, some tests require more.
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-stack_size -Wl,0x1000000")
|
||||
endif()
|
||||
|
||||
# Some Linux-specific Clang settings. We don't want these for OS X.
|
||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
|
||||
|
||||
@ -115,7 +120,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
endif()
|
||||
|
||||
if (EMSCRIPTEN)
|
||||
# Do emit a separate memory initialiser file
|
||||
# Do not emit a separate memory initialiser file
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --memory-init-file 0")
|
||||
# Leave only exported symbols as public and agressively remove others
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -ffunction-sections -Wl,--gc-sections -fvisibility=hidden")
|
||||
@ -133,6 +138,8 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s ALLOW_MEMORY_GROWTH=1")
|
||||
# Disable eval()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DYNAMIC_EXECUTION=1")
|
||||
# Disable greedy exception catcher
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NODEJS_CATCH_EXIT=0")
|
||||
add_definitions(-DETH_EMSCRIPTEN=1)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -40,21 +40,21 @@ The following elementary types exist:
|
||||
|
||||
- ``int<M>``: two's complement signed integer type of ``M`` bits, ``0 < M <= 256``, ``M % 8 == 0``.
|
||||
|
||||
- ``address``: equivalent to ``uint160``, except for the assumed interpretation and language typing.
|
||||
- ``address``: equivalent to ``uint160``, except for the assumed interpretation and language typing. For computing the function selector, ``address`` is used.
|
||||
|
||||
- ``uint``, ``int``: synonyms for ``uint256``, ``int256`` respectively (this shorthand not to be used for computing the function selector).
|
||||
- ``uint``, ``int``: synonyms for ``uint256``, ``int256`` respectively. For computing the function selector, ``uint256`` and ``int256`` have to be used.
|
||||
|
||||
- ``bool``: equivalent to ``uint8`` restricted to the values 0 and 1
|
||||
- ``bool``: equivalent to ``uint8`` restricted to the values 0 and 1. For computing the function selector, ``bool`` is used.
|
||||
|
||||
- ``fixed<M>x<N>``: signed fixed-point decimal number of ``M`` bits, ``8 <= M <= 256``, ``M % 8 ==0``, and ``0 < N <= 80``, which denotes the value ``v`` as ``v / (10 ** N)``.
|
||||
|
||||
- ``ufixed<M>x<N>``: unsigned variant of ``fixed<M>x<N>``.
|
||||
|
||||
- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively (this shorthand not to be used for computing the function selector).
|
||||
- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively. For computing the function selector, ``fixed128x19`` and ``ufixed128x19`` have to be used.
|
||||
|
||||
- ``bytes<M>``: binary type of ``M`` bytes, ``0 < M <= 32``.
|
||||
|
||||
- ``function``: equivalent to ``bytes24``: an address, followed by a function selector
|
||||
- ``function``: an address (20 bytes) folled by a function selector (4 bytes). Encoded identical to ``bytes24``.
|
||||
|
||||
The following (fixed-size) array type exists:
|
||||
|
||||
@ -187,12 +187,12 @@ Given the contract:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract Foo {
|
||||
function bar(bytes3[2] xy) {}
|
||||
function baz(uint32 x, bool y) returns (bool r) { r = x > 32 || y; }
|
||||
function sam(bytes name, bool z, uint[] data) {}
|
||||
function bar(bytes3[2]) public pure {}
|
||||
function baz(uint32 x, bool y) public pure returns (bool r) { r = x > 32 || y; }
|
||||
function sam(bytes, bool, uint[]) public pure {}
|
||||
}
|
||||
|
||||
|
||||
@ -288,6 +288,8 @@ In effect, a log entry using this ABI is described as:
|
||||
- ``topics[n]``: ``EVENT_INDEXED_ARGS[n - 1]`` (``EVENT_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are indexed);
|
||||
- ``data``: ``abi_serialise(EVENT_NON_INDEXED_ARGS)`` (``EVENT_NON_INDEXED_ARGS`` is the series of ``EVENT_ARGS`` that are not indexed, ``abi_serialise`` is the ABI serialisation function used for returning a series of typed values from a function, as described above).
|
||||
|
||||
For all fixed-length Solidity types, the ``EVENT_INDEXED_ARGS`` array contains the 32-byte encoded value directly. However, for *types of dynamic length*, which include ``string``, ``bytes``, and arrays, ``EVENT_INDEXED_ARGS`` will contain the *Keccak hash* of the encoded value, rather than the encoded value directly. This allows applications to efficiently query for values of dynamic-length types (by setting the hash of the encoded value as the topic), but leaves applications unable to decode indexed values they have not queried for. For dynamic-length types, application developers face a trade-off between fast search for predetermined values (if the argument is indexed) and legibility of arbitrary values (which requires that the arguments not be indexed). Developers may overcome this tradeoff and achieve both efficient search and arbitrary legibility by defining events with two arguments — one indexed, one not — intended to hold the same value.
|
||||
|
||||
JSON
|
||||
====
|
||||
|
||||
@ -333,10 +335,10 @@ For example,
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Test {
|
||||
function Test(){ b = 0x12345678901234567890123456789012; }
|
||||
function Test() public { b = 0x12345678901234567890123456789012; }
|
||||
event Event(uint indexed a, bytes32 b);
|
||||
event Event2(uint indexed a, bytes32 b);
|
||||
function foo(uint a) { Event(a, b); }
|
||||
function foo(uint a) public { Event(a, b); }
|
||||
bytes32 b;
|
||||
}
|
||||
|
||||
@ -377,10 +379,14 @@ As an example, the code
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.19;
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
contract Test {
|
||||
struct S { uint a; uint[] b; T[] c; }
|
||||
struct T { uint x; uint y; }
|
||||
function f(S s, T t, uint a) { }
|
||||
function f(S s, T t, uint a) public { }
|
||||
function g() public returns (S s, T t, uint a) {}
|
||||
}
|
||||
|
||||
would result in the JSON:
|
||||
|
@ -23,7 +23,7 @@ arising when writing manual assembly by the following features:
|
||||
|
||||
* functional-style opcodes: ``mul(1, add(2, 3))`` instead of ``push1 3 push1 2 add push1 1 mul``
|
||||
* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)``
|
||||
* access to external variables: ``function f(uint x) { assembly { x := sub(x, 1) } }``
|
||||
* access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }``
|
||||
* labels: ``let x := 10 repeat: x := sub(x, 1) jumpi(repeat, eq(x, 0))``
|
||||
* loops: ``for { let i := 0 } lt(i, x) { i := add(i, 1) } { y := mul(2, y) }``
|
||||
* if statements: ``if slt(x, 0) { x := sub(0, x) }``
|
||||
@ -54,7 +54,7 @@ idea is that assembly libraries will be used to enhance the language in such way
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
library GetCode {
|
||||
function at(address _addr) returns (bytes o_code) {
|
||||
function at(address _addr) public view returns (bytes o_code) {
|
||||
assembly {
|
||||
// retrieve the size of the code, this needs assembly
|
||||
let size := extcodesize(_addr)
|
||||
@ -78,12 +78,12 @@ you really know what you are doing.
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity ^0.4.12;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
library VectorSum {
|
||||
// This function is less efficient because the optimizer currently fails to
|
||||
// remove the bounds checks in array access.
|
||||
function sumSolidity(uint[] _data) returns (uint o_sum) {
|
||||
function sumSolidity(uint[] _data) public view returns (uint o_sum) {
|
||||
for (uint i = 0; i < _data.length; ++i)
|
||||
o_sum += _data[i];
|
||||
}
|
||||
@ -91,7 +91,7 @@ you really know what you are doing.
|
||||
// We know that we only access the array in bounds, so we can avoid the check.
|
||||
// 0x20 needs to be added to an array because the first slot contains the
|
||||
// array length.
|
||||
function sumAsm(uint[] _data) returns (uint o_sum) {
|
||||
function sumAsm(uint[] _data) public view returns (uint o_sum) {
|
||||
for (uint i = 0; i < _data.length; ++i) {
|
||||
assembly {
|
||||
o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
|
||||
@ -100,7 +100,7 @@ you really know what you are doing.
|
||||
}
|
||||
|
||||
// Same as above, but accomplish the entire code within inline assembly.
|
||||
function sumPureAsm(uint[] _data) returns (uint o_sum) {
|
||||
function sumPureAsm(uint[] _data) public view returns (uint o_sum) {
|
||||
assembly {
|
||||
// Load the length (first 32 bytes)
|
||||
let len := mload(_data)
|
||||
@ -388,7 +388,7 @@ changes during the call, and thus references to local variables will be wrong.
|
||||
|
||||
contract C {
|
||||
uint b;
|
||||
function f(uint x) returns (uint r) {
|
||||
function f(uint x) public returns (uint r) {
|
||||
assembly {
|
||||
r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero
|
||||
}
|
||||
@ -447,31 +447,6 @@ will have a wrong impression about the stack height at label ``two``:
|
||||
three:
|
||||
}
|
||||
|
||||
This problem can be fixed by manually adjusting the stack height for the
|
||||
assembler - you can provide a stack height delta that is added
|
||||
to the stack height just prior to the label.
|
||||
Note that you will not have to care about these things if you just use
|
||||
loops and assembly-level functions.
|
||||
|
||||
As an example how this can be done in extreme cases, please see the following.
|
||||
|
||||
.. code::
|
||||
|
||||
{
|
||||
let x := 8
|
||||
jump(two)
|
||||
0 // This code is unreachable but will adjust the stack height correctly
|
||||
one:
|
||||
x := 9 // Now x can be accessed properly.
|
||||
jump(three)
|
||||
pop // Similar negative correction.
|
||||
two:
|
||||
7 // push something onto the stack
|
||||
jump(one)
|
||||
three:
|
||||
pop // We have to pop the manually pushed value here again.
|
||||
}
|
||||
|
||||
Declaring Assembly-Local Variables
|
||||
----------------------------------
|
||||
|
||||
@ -484,10 +459,10 @@ be just ``0``, but it can also be a complex functional-style expression.
|
||||
|
||||
.. code::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f(uint x) returns (uint b) {
|
||||
function f(uint x) public view returns (uint b) {
|
||||
assembly {
|
||||
let v := add(x, 1)
|
||||
mstore(0x80, v)
|
||||
@ -735,10 +710,10 @@ Example:
|
||||
We will follow an example compilation from Solidity to desugared assembly.
|
||||
We consider the runtime bytecode of the following Solidity program::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f(uint x) returns (uint y) {
|
||||
function f(uint x) public pure returns (uint y) {
|
||||
y = 1;
|
||||
for (uint i = 0; i < x; i++)
|
||||
y = 2 * y;
|
||||
|
@ -414,6 +414,10 @@
|
||||
],
|
||||
"released": "2016-09-17"
|
||||
},
|
||||
"0.4.20": {
|
||||
"bugs": [],
|
||||
"released": "2018-02-14"
|
||||
},
|
||||
"0.4.3": {
|
||||
"bugs": [
|
||||
"ZeroFunctionSelector",
|
||||
|
@ -36,12 +36,12 @@ become the new richest.
|
||||
|
||||
mapping (address => uint) pendingWithdrawals;
|
||||
|
||||
function WithdrawalContract() payable {
|
||||
function WithdrawalContract() public payable {
|
||||
richest = msg.sender;
|
||||
mostSent = msg.value;
|
||||
}
|
||||
|
||||
function becomeRichest() payable returns (bool) {
|
||||
function becomeRichest() public payable returns (bool) {
|
||||
if (msg.value > mostSent) {
|
||||
pendingWithdrawals[richest] += msg.value;
|
||||
richest = msg.sender;
|
||||
@ -52,7 +52,7 @@ become the new richest.
|
||||
}
|
||||
}
|
||||
|
||||
function withdraw() {
|
||||
function withdraw() public {
|
||||
uint amount = pendingWithdrawals[msg.sender];
|
||||
// Remember to zero the pending refund before
|
||||
// sending to prevent re-entrancy attacks
|
||||
@ -71,12 +71,12 @@ This is as opposed to the more intuitive sending pattern:
|
||||
address public richest;
|
||||
uint public mostSent;
|
||||
|
||||
function SendContract() payable {
|
||||
function SendContract() public payable {
|
||||
richest = msg.sender;
|
||||
mostSent = msg.value;
|
||||
}
|
||||
|
||||
function becomeRichest() payable returns (bool) {
|
||||
function becomeRichest() public payable returns (bool) {
|
||||
if (msg.value > mostSent) {
|
||||
// This line can cause problems (explained below).
|
||||
richest.transfer(msg.value);
|
||||
@ -157,6 +157,7 @@ restrictions highly readable.
|
||||
/// Make `_newOwner` the new owner of this
|
||||
/// contract.
|
||||
function changeOwner(address _newOwner)
|
||||
public
|
||||
onlyBy(owner)
|
||||
{
|
||||
owner = _newOwner;
|
||||
@ -171,6 +172,7 @@ restrictions highly readable.
|
||||
/// May only be called 6 weeks after
|
||||
/// the contract has been created.
|
||||
function disown()
|
||||
public
|
||||
onlyBy(owner)
|
||||
onlyAfter(creationTime + 6 weeks)
|
||||
{
|
||||
@ -191,6 +193,7 @@ restrictions highly readable.
|
||||
}
|
||||
|
||||
function forceOwnerChange(address _newOwner)
|
||||
public
|
||||
costs(200 ether)
|
||||
{
|
||||
owner = _newOwner;
|
||||
@ -310,6 +313,7 @@ function finishes.
|
||||
|
||||
// Order of the modifiers matters here!
|
||||
function bid()
|
||||
public
|
||||
payable
|
||||
timedTransitions
|
||||
atStage(Stages.AcceptingBlindedBids)
|
||||
@ -318,6 +322,7 @@ function finishes.
|
||||
}
|
||||
|
||||
function reveal()
|
||||
public
|
||||
timedTransitions
|
||||
atStage(Stages.RevealBids)
|
||||
{
|
||||
@ -332,6 +337,7 @@ function finishes.
|
||||
}
|
||||
|
||||
function g()
|
||||
public
|
||||
timedTransitions
|
||||
atStage(Stages.AnotherStage)
|
||||
transitionNext
|
||||
@ -339,6 +345,7 @@ function finishes.
|
||||
}
|
||||
|
||||
function h()
|
||||
public
|
||||
timedTransitions
|
||||
atStage(Stages.AreWeDoneYet)
|
||||
transitionNext
|
||||
@ -346,6 +353,7 @@ function finishes.
|
||||
}
|
||||
|
||||
function i()
|
||||
public
|
||||
timedTransitions
|
||||
atStage(Stages.Finished)
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract OwnedToken {
|
||||
// TokenCreator is a contract type that is defined below.
|
||||
@ -52,7 +52,7 @@ This means that cyclic creation dependencies are impossible.
|
||||
|
||||
// This is the constructor which registers the
|
||||
// creator and the assigned name.
|
||||
function OwnedToken(bytes32 _name) {
|
||||
function OwnedToken(bytes32 _name) public {
|
||||
// State variables are accessed via their name
|
||||
// and not via e.g. this.owner. This also applies
|
||||
// to functions and especially in the constructors,
|
||||
@ -67,7 +67,7 @@ This means that cyclic creation dependencies are impossible.
|
||||
name = _name;
|
||||
}
|
||||
|
||||
function changeName(bytes32 newName) {
|
||||
function changeName(bytes32 newName) public {
|
||||
// Only the creator can alter the name --
|
||||
// the comparison is possible since contracts
|
||||
// are implicitly convertible to addresses.
|
||||
@ -75,7 +75,7 @@ This means that cyclic creation dependencies are impossible.
|
||||
name = newName;
|
||||
}
|
||||
|
||||
function transfer(address newOwner) {
|
||||
function transfer(address newOwner) public {
|
||||
// Only the current owner can transfer the token.
|
||||
if (msg.sender != owner) return;
|
||||
// We also want to ask the creator if the transfer
|
||||
@ -90,25 +90,27 @@ This means that cyclic creation dependencies are impossible.
|
||||
|
||||
contract TokenCreator {
|
||||
function createToken(bytes32 name)
|
||||
public
|
||||
returns (OwnedToken tokenAddress)
|
||||
{
|
||||
// Create a new Token contract and return its address.
|
||||
// From the JavaScript side, the return type is simply
|
||||
// "address", as this is the closest type available in
|
||||
// `address`, as this is the closest type available in
|
||||
// the ABI.
|
||||
return new OwnedToken(name);
|
||||
}
|
||||
|
||||
function changeName(OwnedToken tokenAddress, bytes32 name) {
|
||||
// Again, the external type of "tokenAddress" is
|
||||
// simply "address".
|
||||
function changeName(OwnedToken tokenAddress, bytes32 name) public {
|
||||
// Again, the external type of `tokenAddress` is
|
||||
// simply `address`.
|
||||
tokenAddress.changeName(name);
|
||||
}
|
||||
|
||||
function isTokenTransferOK(
|
||||
address currentOwner,
|
||||
address newOwner
|
||||
) returns (bool ok) {
|
||||
function isTokenTransferOK(address currentOwner, address newOwner)
|
||||
public
|
||||
view
|
||||
returns (bool ok)
|
||||
{
|
||||
// Check some arbitrary condition.
|
||||
address tokenAddress = msg.sender;
|
||||
return (keccak256(newOwner) & 0xff) == (bytes20(tokenAddress) & 0xff);
|
||||
@ -171,10 +173,10 @@ return parameter list for functions.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f(uint a) private returns (uint b) { return a + 1; }
|
||||
function f(uint a) private pure returns (uint b) { return a + 1; }
|
||||
function setData(uint a) internal { data = a; }
|
||||
uint public data;
|
||||
}
|
||||
@ -193,23 +195,23 @@ In the following example, ``D``, can call ``c.getData()`` to retrieve the value
|
||||
uint private data;
|
||||
|
||||
function f(uint a) private returns(uint b) { return a + 1; }
|
||||
function setData(uint a) { data = a; }
|
||||
function setData(uint a) public { data = a; }
|
||||
function getData() public returns(uint) { return data; }
|
||||
function compute(uint a, uint b) internal returns (uint) { return a+b; }
|
||||
}
|
||||
|
||||
contract D {
|
||||
function readData() {
|
||||
function readData() public {
|
||||
C c = new C();
|
||||
uint local = c.f(7); // error: member "f" is not visible
|
||||
uint local = c.f(7); // error: member `f` is not visible
|
||||
c.setData(3);
|
||||
local = c.getData();
|
||||
local = c.compute(3, 5); // error: member "compute" is not visible
|
||||
local = c.compute(3, 5); // error: member `compute` is not visible
|
||||
}
|
||||
}
|
||||
|
||||
contract E is C {
|
||||
function g() {
|
||||
function g() public {
|
||||
C c = new C();
|
||||
uint val = compute(3, 5); // access to internal member (from derived to parent contract)
|
||||
}
|
||||
@ -238,7 +240,7 @@ be done at declaration.
|
||||
|
||||
contract Caller {
|
||||
C c = new C();
|
||||
function f() {
|
||||
function f() public {
|
||||
uint local = c.data();
|
||||
}
|
||||
}
|
||||
@ -254,7 +256,7 @@ it is evaluated as a state variable. If it is accessed externally
|
||||
|
||||
contract C {
|
||||
uint public data;
|
||||
function x() {
|
||||
function x() public {
|
||||
data = 3; // internal access
|
||||
uint val = this.data(); // external access
|
||||
}
|
||||
@ -277,7 +279,7 @@ The next example is a bit more complex:
|
||||
|
||||
It will generate a function of the following form::
|
||||
|
||||
function data(uint arg1, bool arg2, uint arg3) returns (uint a, bytes3 b) {
|
||||
function data(uint arg1, bool arg2, uint arg3) public returns (uint a, bytes3 b) {
|
||||
a = data[arg1][arg2][arg3].a;
|
||||
b = data[arg1][arg2][arg3].b;
|
||||
}
|
||||
@ -302,13 +304,13 @@ inheritable properties of contracts and may be overridden by derived contracts.
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
contract owned {
|
||||
function owned() { owner = msg.sender; }
|
||||
function owned() public { owner = msg.sender; }
|
||||
address owner;
|
||||
|
||||
// This contract only defines a modifier but does not use
|
||||
// it - it will be used in derived contracts.
|
||||
// it: it will be used in derived contracts.
|
||||
// The function body is inserted where the special symbol
|
||||
// "_;" in the definition of a modifier appears.
|
||||
// `_;` in the definition of a modifier appears.
|
||||
// This means that if the owner calls this function, the
|
||||
// function is executed and otherwise, an exception is
|
||||
// thrown.
|
||||
@ -319,11 +321,11 @@ inheritable properties of contracts and may be overridden by derived contracts.
|
||||
}
|
||||
|
||||
contract mortal is owned {
|
||||
// This contract inherits the "onlyOwner"-modifier from
|
||||
// "owned" and applies it to the "close"-function, which
|
||||
// causes that calls to "close" only have an effect if
|
||||
// This contract inherits the `onlyOwner` modifier from
|
||||
// `owned` and applies it to the `close` function, which
|
||||
// causes that calls to `close` only have an effect if
|
||||
// they are made by the stored owner.
|
||||
function close() onlyOwner {
|
||||
function close() public onlyOwner {
|
||||
selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
@ -341,16 +343,16 @@ inheritable properties of contracts and may be overridden by derived contracts.
|
||||
mapping (address => bool) registeredAddresses;
|
||||
uint price;
|
||||
|
||||
function Register(uint initialPrice) { price = initialPrice; }
|
||||
function Register(uint initialPrice) public { price = initialPrice; }
|
||||
|
||||
// It is important to also provide the
|
||||
// "payable" keyword here, otherwise the function will
|
||||
// `payable` keyword here, otherwise the function will
|
||||
// automatically reject all Ether sent to it.
|
||||
function register() payable costs(price) {
|
||||
function register() public payable costs(price) {
|
||||
registeredAddresses[msg.sender] = true;
|
||||
}
|
||||
|
||||
function changePrice(uint _price) onlyOwner {
|
||||
function changePrice(uint _price) public onlyOwner {
|
||||
price = _price;
|
||||
}
|
||||
}
|
||||
@ -365,10 +367,10 @@ inheritable properties of contracts and may be overridden by derived contracts.
|
||||
}
|
||||
|
||||
/// This function is protected by a mutex, which means that
|
||||
/// reentrant calls from within msg.sender.call cannot call f again.
|
||||
/// 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) {
|
||||
function f() public noReentrancy returns (uint) {
|
||||
require(msg.sender.call());
|
||||
return 7;
|
||||
}
|
||||
@ -426,19 +428,27 @@ value types and strings.
|
||||
bytes32 constant myHash = keccak256("abc");
|
||||
}
|
||||
|
||||
.. index:: ! functions
|
||||
|
||||
.. _functions:
|
||||
|
||||
*********
|
||||
Functions
|
||||
*********
|
||||
|
||||
.. index:: ! view function, function;view
|
||||
|
||||
.. _view-functions:
|
||||
|
||||
**************
|
||||
View Functions
|
||||
**************
|
||||
==============
|
||||
|
||||
Functions can be declared ``view`` in which case they promise not to modify the state.
|
||||
|
||||
The following statements are considered modifying the state:
|
||||
|
||||
#. Writing to state variables.
|
||||
#. :ref:`Emitting events. <events>`.
|
||||
#. :ref:`Emitting events <events>`.
|
||||
#. :ref:`Creating other contracts <creating-contracts>`.
|
||||
#. Using ``selfdestruct``.
|
||||
#. Sending Ether via calls.
|
||||
@ -451,7 +461,7 @@ The following statements are considered modifying the state:
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f(uint a, uint b) view returns (uint) {
|
||||
function f(uint a, uint b) public view returns (uint) {
|
||||
return a * (b + 42) + now;
|
||||
}
|
||||
}
|
||||
@ -465,11 +475,12 @@ The following statements are considered modifying the state:
|
||||
.. warning::
|
||||
The compiler does not enforce yet that a ``view`` method is not modifying state.
|
||||
|
||||
.. index:: ! pure function, function;pure
|
||||
|
||||
.. _pure-functions:
|
||||
|
||||
**************
|
||||
Pure Functions
|
||||
**************
|
||||
==============
|
||||
|
||||
Functions can be declared ``pure`` in which case they promise not to read from or modify the state.
|
||||
|
||||
@ -486,7 +497,7 @@ In addition to the list of state modifying statements explained above, the follo
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f(uint a, uint b) pure returns (uint) {
|
||||
function f(uint a, uint b) public pure returns (uint) {
|
||||
return a * (b + 42);
|
||||
}
|
||||
}
|
||||
@ -498,9 +509,8 @@ In addition to the list of state modifying statements explained above, the follo
|
||||
|
||||
.. _fallback-function:
|
||||
|
||||
*****************
|
||||
Fallback Function
|
||||
*****************
|
||||
=================
|
||||
|
||||
A contract can have exactly one unnamed function. This function cannot have
|
||||
arguments and cannot return anything.
|
||||
@ -551,9 +561,9 @@ Please ensure you test your fallback function thoroughly to ensure the execution
|
||||
// This function is called for all messages sent to
|
||||
// this contract (there is no other function).
|
||||
// Sending Ether to this contract will cause an exception,
|
||||
// because the fallback function does not have the "payable"
|
||||
// because the fallback function does not have the `payable`
|
||||
// modifier.
|
||||
function() { x = 1; }
|
||||
function() public { x = 1; }
|
||||
uint x;
|
||||
}
|
||||
|
||||
@ -561,11 +571,11 @@ Please ensure you test your fallback function thoroughly to ensure the execution
|
||||
// This contract keeps all Ether sent to it with no way
|
||||
// to get it back.
|
||||
contract Sink {
|
||||
function() payable { }
|
||||
function() public payable { }
|
||||
}
|
||||
|
||||
contract Caller {
|
||||
function callTest(Test test) {
|
||||
function callTest(Test test) public {
|
||||
test.call(0xabcdef01); // hash does not exist
|
||||
// results in test.x becoming == 1.
|
||||
|
||||
@ -577,6 +587,85 @@ Please ensure you test your fallback function thoroughly to ensure the execution
|
||||
}
|
||||
}
|
||||
|
||||
.. index:: ! overload
|
||||
|
||||
.. _overload-function:
|
||||
|
||||
Function Overloading
|
||||
====================
|
||||
|
||||
A Contract can have multiple functions of the same name but with different arguments.
|
||||
This also applies to inherited functions. The following example shows overloading of the
|
||||
``f`` function in the scope of contract ``A``.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract A {
|
||||
function f(uint _in) public pure returns (uint out) {
|
||||
out = 1;
|
||||
}
|
||||
|
||||
function f(uint _in, bytes32 _key) public pure returns (uint out) {
|
||||
out = 2;
|
||||
}
|
||||
}
|
||||
|
||||
Overloaded functions are also present in the external interface. It is an error if two
|
||||
externally visible functions differ by their Solidity types but not by their external types.
|
||||
|
||||
::
|
||||
|
||||
// This will not compile
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract A {
|
||||
function f(B _in) public pure returns (B out) {
|
||||
out = _in;
|
||||
}
|
||||
|
||||
function f(address _in) public pure returns (address out) {
|
||||
out = _in;
|
||||
}
|
||||
}
|
||||
|
||||
contract B {
|
||||
}
|
||||
|
||||
|
||||
Both ``f`` function overloads above end up accepting the address type for the ABI although
|
||||
they are considered different inside Solidity.
|
||||
|
||||
Overload resolution and Argument matching
|
||||
-----------------------------------------
|
||||
|
||||
Overloaded functions are selected by matching the function declarations in the current scope
|
||||
to the arguments supplied in the function call. Functions are selected as overload candidates
|
||||
if all arguments can be implicitly converted to the expected types. If there is not exactly one
|
||||
candidate, resolution fails.
|
||||
|
||||
.. note::
|
||||
Return parameters are not taken into account for overload resolution.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract A {
|
||||
function f(uint8 _in) public pure returns (uint8 out) {
|
||||
out = _in;
|
||||
}
|
||||
|
||||
function f(uint256 _in) public pure returns (uint256 out) {
|
||||
out = _in;
|
||||
}
|
||||
}
|
||||
|
||||
Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8``
|
||||
and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly
|
||||
converted to ``uint8``.
|
||||
|
||||
.. index:: ! event
|
||||
|
||||
.. _events:
|
||||
@ -634,7 +723,7 @@ All non-indexed arguments will be stored in the data part of the log.
|
||||
uint _value
|
||||
);
|
||||
|
||||
function deposit(bytes32 _id) payable {
|
||||
function deposit(bytes32 _id) public payable {
|
||||
// Any call to this function (even deeply nested) can
|
||||
// be detected from the JavaScript API by filtering
|
||||
// for `Deposit` to be called.
|
||||
@ -655,7 +744,7 @@ The use in the JavaScript API would be as follows:
|
||||
// watch for changes
|
||||
event.watch(function(error, result){
|
||||
// result will contain various information
|
||||
// including the argumets given to the Deposit
|
||||
// including the argumets given to the `Deposit`
|
||||
// call.
|
||||
if (!error)
|
||||
console.log(result);
|
||||
@ -683,7 +772,7 @@ as topics. The event call above can be performed in the same way as
|
||||
pragma solidity ^0.4.10;
|
||||
|
||||
contract C {
|
||||
function f() {
|
||||
function f() public payable {
|
||||
bytes32 _id = 0x420042;
|
||||
log3(
|
||||
bytes32(msg.value),
|
||||
@ -727,14 +816,14 @@ Details are given in the following example.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract owned {
|
||||
function owned() { owner = msg.sender; }
|
||||
address owner;
|
||||
}
|
||||
|
||||
// Use "is" to derive from another contract. Derived
|
||||
// Use `is` to derive from another contract. Derived
|
||||
// contracts can access all non-private members including
|
||||
// internal functions and state variables. These cannot be
|
||||
// accessed externally via `this`, though.
|
||||
@ -749,20 +838,20 @@ Details are given in the following example.
|
||||
// without body. If a contract does not implement all
|
||||
// functions it can only be used as an interface.
|
||||
contract Config {
|
||||
function lookup(uint id) returns (address adr);
|
||||
function lookup(uint id) public returns (address adr);
|
||||
}
|
||||
|
||||
contract NameReg {
|
||||
function register(bytes32 name);
|
||||
function unregister();
|
||||
function register(bytes32 name) public;
|
||||
function unregister() public;
|
||||
}
|
||||
|
||||
// Multiple inheritance is possible. Note that "owned" is
|
||||
// also a base class of "mortal", yet there is only a single
|
||||
// instance of "owned" (as for virtual inheritance in C++).
|
||||
// Multiple inheritance is possible. Note that `owned` is
|
||||
// also a base class of `mortal`, yet there is only a single
|
||||
// instance of `owned` (as for virtual inheritance in C++).
|
||||
contract named is owned, mortal {
|
||||
function named(bytes32 name) {
|
||||
Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);
|
||||
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
||||
NameReg(config.lookup(1)).register(name);
|
||||
}
|
||||
|
||||
@ -771,9 +860,9 @@ Details are given in the following example.
|
||||
// types of output parameters, that causes an error.
|
||||
// Both local and message-based function calls take these overrides
|
||||
// into account.
|
||||
function kill() {
|
||||
function kill() public {
|
||||
if (msg.sender == owner) {
|
||||
Config config = Config(0xd5f9d8d94886e70b06e474c3fb14fd43e2f23970);
|
||||
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
|
||||
NameReg(config.lookup(1)).unregister();
|
||||
// It is still possible to call a specific
|
||||
// overridden function.
|
||||
@ -786,11 +875,11 @@ Details are given in the following example.
|
||||
// provided in the header (or modifier-invocation-style at
|
||||
// the constructor of the derived contract (see below)).
|
||||
contract PriceFeed is owned, mortal, named("GoldFeed") {
|
||||
function updateInfo(uint newInfo) {
|
||||
function updateInfo(uint newInfo) public {
|
||||
if (msg.sender == owner) info = newInfo;
|
||||
}
|
||||
|
||||
function get() constant returns(uint r) { return info; }
|
||||
function get() public view returns(uint r) { return info; }
|
||||
|
||||
uint info;
|
||||
}
|
||||
@ -802,22 +891,22 @@ seen in the following example::
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract owned {
|
||||
function owned() { owner = msg.sender; }
|
||||
function owned() public { owner = msg.sender; }
|
||||
address owner;
|
||||
}
|
||||
|
||||
contract mortal is owned {
|
||||
function kill() {
|
||||
function kill() public {
|
||||
if (msg.sender == owner) selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
|
||||
contract Base1 is mortal {
|
||||
function kill() { /* do cleanup 1 */ mortal.kill(); }
|
||||
function kill() public { /* do cleanup 1 */ mortal.kill(); }
|
||||
}
|
||||
|
||||
contract Base2 is mortal {
|
||||
function kill() { /* do cleanup 2 */ mortal.kill(); }
|
||||
function kill() public { /* do cleanup 2 */ mortal.kill(); }
|
||||
}
|
||||
|
||||
contract Final is Base1, Base2 {
|
||||
@ -831,34 +920,34 @@ derived override, but this function will bypass
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract owned {
|
||||
function owned() { owner = msg.sender; }
|
||||
function owned() public { owner = msg.sender; }
|
||||
address owner;
|
||||
}
|
||||
|
||||
contract mortal is owned {
|
||||
function kill() {
|
||||
function kill() public {
|
||||
if (msg.sender == owner) selfdestruct(owner);
|
||||
}
|
||||
}
|
||||
|
||||
contract Base1 is mortal {
|
||||
function kill() { /* do cleanup 1 */ super.kill(); }
|
||||
function kill() public { /* do cleanup 1 */ super.kill(); }
|
||||
}
|
||||
|
||||
|
||||
contract Base2 is mortal {
|
||||
function kill() { /* do cleanup 2 */ super.kill(); }
|
||||
function kill() public { /* do cleanup 2 */ super.kill(); }
|
||||
}
|
||||
|
||||
contract Final is Base2, Base1 {
|
||||
contract Final is Base1, Base2 {
|
||||
}
|
||||
|
||||
If ``Base1`` calls a function of ``super``, it does not simply
|
||||
If ``Base2`` calls a function of ``super``, it does not simply
|
||||
call this function on one of its base contracts. Rather, it
|
||||
calls this function on the next base contract in the final
|
||||
inheritance graph, so it will call ``Base2.kill()`` (note that
|
||||
inheritance graph, so it will call ``Base1.kill()`` (note that
|
||||
the final inheritance sequence is -- starting with the most
|
||||
derived contract: Final, Base1, Base2, mortal, owned).
|
||||
derived contract: Final, Base2, Base1, mortal, owned).
|
||||
The actual function that is called when using super is
|
||||
not known in the context of the class where it is used,
|
||||
although its type is known. This is similar for ordinary
|
||||
@ -876,11 +965,11 @@ the base constructors. This can be done in two ways::
|
||||
|
||||
contract Base {
|
||||
uint x;
|
||||
function Base(uint _x) { x = _x; }
|
||||
function Base(uint _x) public { x = _x; }
|
||||
}
|
||||
|
||||
contract Derived is Base(7) {
|
||||
function Derived(uint _y) Base(_y * _y) {
|
||||
function Derived(uint _y) Base(_y * _y) public {
|
||||
}
|
||||
}
|
||||
|
||||
@ -945,7 +1034,7 @@ Contract functions can lack an implementation as in the following example (note
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Feline {
|
||||
function utterance() returns (bytes32);
|
||||
function utterance() public returns (bytes32);
|
||||
}
|
||||
|
||||
Such contracts cannot be compiled (even if they contain
|
||||
@ -955,11 +1044,11 @@ but they can be used as base contracts::
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Feline {
|
||||
function utterance() returns (bytes32);
|
||||
function utterance() public returns (bytes32);
|
||||
}
|
||||
|
||||
contract Cat is Feline {
|
||||
function utterance() returns (bytes32) { return "miaow"; }
|
||||
function utterance() public returns (bytes32) { return "miaow"; }
|
||||
}
|
||||
|
||||
If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract.
|
||||
@ -990,7 +1079,7 @@ Interfaces are denoted by their own keyword:
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
interface Token {
|
||||
function transfer(address recipient, uint amount);
|
||||
function transfer(address recipient, uint amount) public;
|
||||
}
|
||||
|
||||
Contracts can inherit interfaces as they would inherit other contracts.
|
||||
@ -1011,7 +1100,11 @@ is executed in the context of the calling contract, i.e. ``this`` points to the
|
||||
calling contract, and especially the storage from the calling contract can be
|
||||
accessed. As a library is an isolated piece of source code, it can only access
|
||||
state variables of the calling contract if they are explicitly supplied (it
|
||||
would have no way to name them, otherwise).
|
||||
would have no way to name them, otherwise). Library functions can only be
|
||||
called directly (i.e. without the use of ``DELEGATECALL``) if they do not modify
|
||||
the state (i.e. if they are ``view`` or ``pure`` functions),
|
||||
because libraries are assumed to be stateless. In particular, it is
|
||||
not possible to destroy a library unless Solidity's type system is circumvented.
|
||||
|
||||
Libraries can be seen as implicit base contracts of the contracts that use them.
|
||||
They will not be explicitly visible in the inheritance hierarchy, but calls
|
||||
@ -1022,7 +1115,7 @@ if the library were a base contract. Of course, calls to internal functions
|
||||
use the internal calling convention, which means that all internal types
|
||||
can be passed and memory types will be passed by reference and not copied.
|
||||
To realize this in the EVM, code of internal library functions
|
||||
and all functions called from therein will be pulled into the calling
|
||||
and all functions called from therein will at compile time be pulled into the calling
|
||||
contract, and a regular ``JUMP`` call will be used instead of a ``DELEGATECALL``.
|
||||
|
||||
.. index:: using for, set
|
||||
@ -1033,7 +1126,7 @@ more advanced example to implement a set).
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
library Set {
|
||||
// We define a new struct datatype that will be used to
|
||||
@ -1044,9 +1137,10 @@ more advanced example to implement a set).
|
||||
// reference" and thus only its storage address and not
|
||||
// its contents is passed as part of the call. This is a
|
||||
// special feature of library functions. It is idiomatic
|
||||
// to call the first parameter 'self', if the function can
|
||||
// to call the first parameter `self`, if the function can
|
||||
// be seen as a method of that object.
|
||||
function insert(Data storage self, uint value)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
if (self.flags[value])
|
||||
@ -1056,6 +1150,7 @@ more advanced example to implement a set).
|
||||
}
|
||||
|
||||
function remove(Data storage self, uint value)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
if (!self.flags[value])
|
||||
@ -1065,6 +1160,8 @@ more advanced example to implement a set).
|
||||
}
|
||||
|
||||
function contains(Data storage self, uint value)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return self.flags[value];
|
||||
@ -1074,7 +1171,7 @@ more advanced example to implement a set).
|
||||
contract C {
|
||||
Set.Data knownValues;
|
||||
|
||||
function register(uint value) {
|
||||
function register(uint value) public {
|
||||
// The library functions can be called without a
|
||||
// specific instance of the library, since the
|
||||
// "instance" will be the current contract.
|
||||
@ -1084,7 +1181,7 @@ more advanced example to implement a set).
|
||||
}
|
||||
|
||||
Of course, you do not have to follow this way to use
|
||||
libraries - they can also be used without defining struct
|
||||
libraries: they can also be used without defining struct
|
||||
data types. Functions also work without any storage
|
||||
reference parameters, and they can have multiple storage reference
|
||||
parameters and in any position.
|
||||
@ -1103,19 +1200,19 @@ custom types without the overhead of external function calls:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
library BigInt {
|
||||
struct bigint {
|
||||
uint[] limbs;
|
||||
}
|
||||
|
||||
function fromUint(uint x) internal returns (bigint r) {
|
||||
function fromUint(uint x) internal pure returns (bigint r) {
|
||||
r.limbs = new uint[](1);
|
||||
r.limbs[0] = x;
|
||||
}
|
||||
|
||||
function add(bigint _a, bigint _b) internal returns (bigint r) {
|
||||
function add(bigint _a, bigint _b) internal pure returns (bigint r) {
|
||||
r.limbs = new uint[](max(_a.limbs.length, _b.limbs.length));
|
||||
uint carry = 0;
|
||||
for (uint i = 0; i < r.limbs.length; ++i) {
|
||||
@ -1137,11 +1234,11 @@ custom types without the overhead of external function calls:
|
||||
}
|
||||
}
|
||||
|
||||
function limb(bigint _a, uint _limb) internal returns (uint) {
|
||||
function limb(bigint _a, uint _limb) internal pure returns (uint) {
|
||||
return _limb < _a.limbs.length ? _a.limbs[_limb] : 0;
|
||||
}
|
||||
|
||||
function max(uint a, uint b) private returns (uint) {
|
||||
function max(uint a, uint b) private pure returns (uint) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
}
|
||||
@ -1149,7 +1246,7 @@ custom types without the overhead of external function calls:
|
||||
contract C {
|
||||
using BigInt for BigInt.bigint;
|
||||
|
||||
function f() {
|
||||
function f() public pure {
|
||||
var x = BigInt.fromUint(7);
|
||||
var y = BigInt.fromUint(uint(-1));
|
||||
var z = x.add(y);
|
||||
@ -1175,6 +1272,30 @@ Restrictions for libraries in comparison to contracts:
|
||||
|
||||
(These might be lifted at a later point.)
|
||||
|
||||
Call Protection For Libraries
|
||||
=============================
|
||||
|
||||
As mentioned in the introduction, if a library's code is executed
|
||||
using a ``CALL`` instead of a ``DELEGATECALL`` or ``CALLCODE``,
|
||||
it will revert unless a ``view`` or ``pure`` function is called.
|
||||
|
||||
The EVM does not provide a direct way for a contract to detect
|
||||
whether it was called using ``CALL`` or not, but a contract
|
||||
can use the ``ADDRESS`` opcode to find out "where" it is
|
||||
currently running. The generated code compares this address
|
||||
to the address used at construction time to determine the mode
|
||||
of calling.
|
||||
|
||||
More specifically, the runtime code of a library always starts
|
||||
with a push instruction, which is a zero of 20 bytes at
|
||||
compilation time. When the deploy code runs, this constant
|
||||
is replaced in memory by the current address and this
|
||||
modified code is stored in the contract. At runtime,
|
||||
this causes the deploy time address to be the first
|
||||
constant to be pushed onto the stack and the dispatcher
|
||||
code compares the current address against this constant
|
||||
for any non-view and non-pure function.
|
||||
|
||||
.. index:: ! using for, library
|
||||
|
||||
.. _using-for:
|
||||
@ -1207,13 +1328,14 @@ available without having to add further code.
|
||||
Let us rewrite the set example from the
|
||||
:ref:`libraries` in this way::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
// This is the same code as before, just without comments
|
||||
library Set {
|
||||
struct Data { mapping(uint => bool) flags; }
|
||||
|
||||
function insert(Data storage self, uint value)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
if (self.flags[value])
|
||||
@ -1223,6 +1345,7 @@ Let us rewrite the set example from the
|
||||
}
|
||||
|
||||
function remove(Data storage self, uint value)
|
||||
public
|
||||
returns (bool)
|
||||
{
|
||||
if (!self.flags[value])
|
||||
@ -1232,6 +1355,8 @@ Let us rewrite the set example from the
|
||||
}
|
||||
|
||||
function contains(Data storage self, uint value)
|
||||
public
|
||||
view
|
||||
returns (bool)
|
||||
{
|
||||
return self.flags[value];
|
||||
@ -1242,21 +1367,25 @@ Let us rewrite the set example from the
|
||||
using Set for Set.Data; // this is the crucial change
|
||||
Set.Data knownValues;
|
||||
|
||||
function register(uint value) {
|
||||
function register(uint value) public {
|
||||
// Here, all variables of type Set.Data have
|
||||
// corresponding member functions.
|
||||
// The following function call is identical to
|
||||
// Set.insert(knownValues, value)
|
||||
// `Set.insert(knownValues, value)`
|
||||
require(knownValues.insert(value));
|
||||
}
|
||||
}
|
||||
|
||||
It is also possible to extend elementary types in that way::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
library Search {
|
||||
function indexOf(uint[] storage self, uint value) returns (uint) {
|
||||
function indexOf(uint[] storage self, uint value)
|
||||
public
|
||||
view
|
||||
returns (uint)
|
||||
{
|
||||
for (uint i = 0; i < self.length; i++)
|
||||
if (self[i] == value) return i;
|
||||
return uint(-1);
|
||||
@ -1267,11 +1396,11 @@ It is also possible to extend elementary types in that way::
|
||||
using Search for uint[];
|
||||
uint[] data;
|
||||
|
||||
function append(uint value) {
|
||||
function append(uint value) public {
|
||||
data.push(value);
|
||||
}
|
||||
|
||||
function replace(uint _old, uint _new) {
|
||||
function replace(uint _old, uint _new) public {
|
||||
// This performs the library function call
|
||||
uint index = data.indexOf(_old);
|
||||
if (index == uint(-1))
|
||||
|
@ -50,8 +50,10 @@ and instead, ``git rebase`` your branch.
|
||||
Additionally, if you are writing a new feature, please ensure you write appropriate
|
||||
Boost test cases and place them under ``test/``.
|
||||
|
||||
However, if you are making a larger change, please consult with the Gitter
|
||||
channel, first.
|
||||
However, if you are making a larger change, please consult with the `Solidity Development Gitter channel
|
||||
<https://gitter.im/ethereum/solidity-dev>`_ (different from the one mentioned above, this on is
|
||||
focused on compiler and language development instead of language use) first.
|
||||
|
||||
|
||||
Finally, please make sure you respect the `coding standards
|
||||
<https://raw.githubusercontent.com/ethereum/cpp-ethereum/develop/CodingStandards.txt>`_
|
||||
|
@ -20,10 +20,10 @@ For example, suppose we want our contract to
|
||||
accept one kind of external calls with two integers, we would write
|
||||
something like::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract Simple {
|
||||
function taker(uint _a, uint _b) {
|
||||
function taker(uint _a, uint _b) public pure {
|
||||
// do something with _a and _b.
|
||||
}
|
||||
}
|
||||
@ -36,10 +36,14 @@ The output parameters can be declared with the same syntax after the
|
||||
the sum and the product of the two given integers, then we would
|
||||
write::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract Simple {
|
||||
function arithmetics(uint _a, uint _b) returns (uint o_sum, uint o_product) {
|
||||
function arithmetics(uint _a, uint _b)
|
||||
public
|
||||
pure
|
||||
returns (uint o_sum, uint o_product)
|
||||
{
|
||||
o_sum = _a + _b;
|
||||
o_product = _a * _b;
|
||||
}
|
||||
@ -95,11 +99,11 @@ Internal Function Calls
|
||||
Functions of the current contract can be called directly ("internally"), also recursively, as seen in
|
||||
this nonsensical example::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function g(uint a) returns (uint ret) { return f(); }
|
||||
function f() returns (uint ret) { return g(7) + f(); }
|
||||
function g(uint a) public pure returns (uint ret) { return f(); }
|
||||
function f() internal pure returns (uint ret) { return g(7) + f(); }
|
||||
}
|
||||
|
||||
These function calls are translated into simple jumps inside the EVM. This has
|
||||
@ -125,13 +129,13 @@ the gas can be specified with special options ``.value()`` and ``.gas()``, respe
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract InfoFeed {
|
||||
function info() payable returns (uint ret) { return 42; }
|
||||
function info() public payable returns (uint ret) { return 42; }
|
||||
}
|
||||
|
||||
contract Consumer {
|
||||
InfoFeed feed;
|
||||
function setFeed(address addr) { feed = InfoFeed(addr); }
|
||||
function callFeed() { feed.info.value(10).gas(800)(); }
|
||||
function setFeed(address addr) public { feed = InfoFeed(addr); }
|
||||
function callFeed() public { feed.info.value(10).gas(800)(); }
|
||||
}
|
||||
|
||||
The modifier ``payable`` has to be used for ``info``, because otherwise, the `.value()`
|
||||
@ -180,11 +184,11 @@ parameters from the function declaration, but can be in arbitrary order.
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function f(uint key, uint value) {
|
||||
function f(uint key, uint value) public {
|
||||
// ...
|
||||
}
|
||||
|
||||
function g() {
|
||||
function g() public {
|
||||
// named arguments
|
||||
f({value: 2, key: 3});
|
||||
}
|
||||
@ -198,11 +202,11 @@ Those parameters will still be present on the stack, but they are inaccessible.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
// omitted name for parameter
|
||||
function func(uint k, uint) returns(uint) {
|
||||
function func(uint k, uint) public pure returns(uint) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
@ -225,7 +229,7 @@ creation-dependencies are not possible.
|
||||
|
||||
contract D {
|
||||
uint x;
|
||||
function D(uint a) payable {
|
||||
function D(uint a) public payable {
|
||||
x = a;
|
||||
}
|
||||
}
|
||||
@ -233,11 +237,11 @@ creation-dependencies are not possible.
|
||||
contract C {
|
||||
D d = new D(4); // will be executed as part of C's constructor
|
||||
|
||||
function createD(uint arg) {
|
||||
function createD(uint arg) public {
|
||||
D newD = new D(arg);
|
||||
}
|
||||
|
||||
function createAndEndowD(uint arg, uint amount) payable {
|
||||
function createAndEndowD(uint arg, uint amount) public payable {
|
||||
// Send ether along with the creation
|
||||
D newD = (new D).value(amount)(arg);
|
||||
}
|
||||
@ -270,16 +274,16 @@ Destructuring Assignments and Returning Multiple Values
|
||||
|
||||
Solidity internally allows tuple types, i.e. a list of objects of potentially different types whose size is a constant at compile-time. Those tuples can be used to return multiple values at the same time and also assign them to multiple variables (or LValues in general) at the same time::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
uint[] data;
|
||||
|
||||
function f() returns (uint, bool, uint) {
|
||||
function f() public pure returns (uint, bool, uint) {
|
||||
return (7, true, 2);
|
||||
}
|
||||
|
||||
function g() {
|
||||
function g() public {
|
||||
// Declares and assigns the variables. Specifying the type explicitly is not possible.
|
||||
var (x, b, y) = f();
|
||||
// Assigns to a pre-existing variable.
|
||||
@ -291,6 +295,7 @@ Solidity internally allows tuple types, i.e. a list of objects of potentially di
|
||||
// the rest of the values are discarded.
|
||||
(data.length,) = f(); // Sets the length to 7
|
||||
// The same can be done on the left side.
|
||||
// If the tuple begins in an empty component, the beginning values are discarded.
|
||||
(,data[3]) = f(); // Sets data[3] to 2
|
||||
// Components can only be left out at the left-hand-side of assignments, with
|
||||
// one exception:
|
||||
@ -326,10 +331,10 @@ As a result, the following code is illegal and cause the compiler to throw an er
|
||||
|
||||
// This will not compile
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract ScopingErrors {
|
||||
function scoping() {
|
||||
function scoping() public {
|
||||
uint i = 0;
|
||||
|
||||
while (i++ < 1) {
|
||||
@ -341,7 +346,7 @@ As a result, the following code is illegal and cause the compiler to throw an er
|
||||
}
|
||||
}
|
||||
|
||||
function minimalScoping() {
|
||||
function minimalScoping() public {
|
||||
{
|
||||
uint same2 = 0;
|
||||
}
|
||||
@ -351,7 +356,7 @@ As a result, the following code is illegal and cause the compiler to throw an er
|
||||
}
|
||||
}
|
||||
|
||||
function forLoopScoping() {
|
||||
function forLoopScoping() public {
|
||||
for (uint same3 = 0; same3 < 1; same3++) {
|
||||
}
|
||||
|
||||
@ -366,7 +371,7 @@ As a result, the following code is legal, despite being poorly written::
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function foo() returns (uint) {
|
||||
function foo() public pure returns (uint) {
|
||||
// baz is implicitly initialized as 0
|
||||
uint bar = 5;
|
||||
if (true) {
|
||||
@ -412,7 +417,7 @@ and how ``assert`` can be used for internal error checking::
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract Sharer {
|
||||
function sendHalf(address addr) payable returns (uint balance) {
|
||||
function sendHalf(address addr) public payable returns (uint balance) {
|
||||
require(msg.value % 2 == 0); // Only allow even numbers
|
||||
uint balanceBeforeTransfer = this.balance;
|
||||
addr.transfer(msg.value / 2);
|
||||
|
@ -39,12 +39,13 @@ This is just the bytecode "data" sent along with the request.
|
||||
Is there a decompiler available?
|
||||
================================
|
||||
|
||||
There is no decompiler to Solidity. This is in principle possible
|
||||
to some degree, but for example variable names will be lost and
|
||||
great effort will be necessary to make it look similar to
|
||||
the original source code.
|
||||
There is no exact decompiler to Solidity, but
|
||||
`Porosity <https://github.com/comaeio/porosity>`_ is close.
|
||||
Because some information like variable names, comments, and
|
||||
source code formatting is lost in the compilation process,
|
||||
it is not possible to completely recover the original source code.
|
||||
|
||||
Bytecode can be decompiled to opcodes, a service that is provided by
|
||||
Bytecode can be disassembled to opcodes, a service that is provided by
|
||||
several blockchain explorers.
|
||||
|
||||
Contracts on the blockchain should have their original source
|
||||
@ -111,10 +112,10 @@ array in the return statement. Pretty cool, huh?
|
||||
|
||||
Example::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f() returns (uint8[5]) {
|
||||
function f() public pure returns (uint8[5]) {
|
||||
string[4] memory adaArr = ["This", "is", "an", "array"];
|
||||
return ([1, 2, 3, 4, 5]);
|
||||
}
|
||||
@ -190,11 +191,11 @@ you should always convert it to a ``bytes`` first::
|
||||
contract C {
|
||||
string s;
|
||||
|
||||
function append(byte c) {
|
||||
function append(byte c) public {
|
||||
bytes(s).push(c);
|
||||
}
|
||||
|
||||
function set(uint i, byte c) {
|
||||
function set(uint i, byte c) public {
|
||||
bytes(s)[i] = c;
|
||||
}
|
||||
}
|
||||
@ -232,12 +233,14 @@ situation.
|
||||
|
||||
If you do not want to throw, you can return a pair::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
uint[] counters;
|
||||
|
||||
function getCounter(uint index)
|
||||
public
|
||||
view
|
||||
returns (uint counter, bool error) {
|
||||
if (index >= counters.length)
|
||||
return (0, true);
|
||||
@ -245,7 +248,7 @@ If you do not want to throw, you can return a pair::
|
||||
return (counters[index], false);
|
||||
}
|
||||
|
||||
function checkCounter(uint index) {
|
||||
function checkCounter(uint index) public view {
|
||||
var (counter, error) = getCounter(index);
|
||||
if (error) {
|
||||
// ...
|
||||
@ -316,11 +319,11 @@ Example::
|
||||
uint[] data1;
|
||||
uint[] data2;
|
||||
|
||||
function appendOne() {
|
||||
function appendOne() public {
|
||||
append(data1);
|
||||
}
|
||||
|
||||
function appendTwo() {
|
||||
function appendTwo() public {
|
||||
append(data2);
|
||||
}
|
||||
|
||||
@ -349,7 +352,7 @@ be created in memory, although it will be created in storage::
|
||||
uint someVariable;
|
||||
uint[] data;
|
||||
|
||||
function f() {
|
||||
function f() public {
|
||||
uint[] x;
|
||||
x.push(2);
|
||||
data = x;
|
||||
@ -375,7 +378,7 @@ The correct way to do this is the following::
|
||||
uint someVariable;
|
||||
uint[] data;
|
||||
|
||||
function f() {
|
||||
function f() public {
|
||||
uint[] x = data;
|
||||
x.push(2);
|
||||
}
|
||||
@ -431,14 +434,14 @@ What happens to a ``struct``'s mapping when copying over a ``struct``?
|
||||
|
||||
This is a very interesting question. Suppose that we have a contract field set up like such::
|
||||
|
||||
struct user {
|
||||
struct User {
|
||||
mapping(string => string) comments;
|
||||
}
|
||||
|
||||
function somefunction {
|
||||
user user1;
|
||||
function somefunction public {
|
||||
User user1;
|
||||
user1.comments["Hello"] = "World";
|
||||
user user2 = user1;
|
||||
User user2 = user1;
|
||||
}
|
||||
|
||||
In this case, the mapping of the struct being copied over into the userList is ignored as there is no "list of mapped keys".
|
||||
@ -456,13 +459,13 @@ In this example::
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract B {
|
||||
function B() payable {}
|
||||
function B() public payable {}
|
||||
}
|
||||
|
||||
contract A {
|
||||
address child;
|
||||
|
||||
function test() {
|
||||
function test() public {
|
||||
child = (new B).value(10)(); //construct a new B with 10 wei
|
||||
}
|
||||
}
|
||||
@ -501,17 +504,17 @@ Can a contract pass an array (static size) or string or ``bytes`` (dynamic size)
|
||||
Sure. Take care that if you cross the memory / storage boundary,
|
||||
independent copies will be created::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
uint[20] x;
|
||||
|
||||
function f() {
|
||||
function f() public {
|
||||
g(x);
|
||||
h(x);
|
||||
}
|
||||
|
||||
function g(uint[20] y) internal {
|
||||
function g(uint[20] y) internal pure {
|
||||
y[2] = 3;
|
||||
}
|
||||
|
||||
|
@ -27,14 +27,19 @@ ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
|
||||
FunctionDefinition = 'function' Identifier? ParameterList
|
||||
( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' )*
|
||||
( 'returns' ParameterList )? ( ';' | Block )
|
||||
EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';'
|
||||
EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';'
|
||||
|
||||
EnumValue = Identifier
|
||||
EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}'
|
||||
|
||||
IndexedParameterList = '(' ( TypeName 'indexed'? Identifier? (',' TypeName 'indexed'? Identifier?)* )? ')'
|
||||
ParameterList = '(' ( TypeName Identifier? (',' TypeName Identifier?)* )? ')'
|
||||
TypeNameList = '(' ( TypeName (',' TypeName )* )? ')'
|
||||
ParameterList = '(' ( Parameter (',' Parameter)* )? ')'
|
||||
Parameter = TypeName StorageLocation? Identifier?
|
||||
|
||||
EventParameterList = '(' ( EventParameter (',' EventParameter )* )? ')'
|
||||
EventParameter = TypeName 'indexed'? Identifier?
|
||||
|
||||
FunctionTypeParameterList = '(' ( FunctionTypeParameter (',' FunctionTypeParameter )* )? ')'
|
||||
FunctionTypeParameter = TypeName StorageLocation?
|
||||
|
||||
// semantic restriction: mappings and structs (recursively) containing mappings
|
||||
// are not allowed in argument lists
|
||||
@ -50,8 +55,8 @@ UserDefinedTypeName = Identifier ( '.' Identifier )*
|
||||
|
||||
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
||||
ArrayTypeName = TypeName '[' Expression? ']'
|
||||
FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )*
|
||||
( 'returns' TypeNameList )?
|
||||
FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )*
|
||||
( 'returns' FunctionTypeParameterList )?
|
||||
StorageLocation = 'memory' | 'storage'
|
||||
StateMutability = 'pure' | 'constant' | 'view' | 'payable'
|
||||
|
||||
|
@ -21,6 +21,14 @@ crowdfunding, blind auctions, multi-signature wallets and more.
|
||||
`Remix <https://remix.ethereum.org/>`_
|
||||
(it can take a while to load, please be patient).
|
||||
|
||||
.. warning::
|
||||
Since software is written by humans, it can have bugs. Thus, also
|
||||
smart contracts should be created following well-known best-practices in
|
||||
software development. This includes code review, testing, audits and correctness proofs.
|
||||
Also note that users are sometimes more confident in code than its authors.
|
||||
Finally, blockchains have their own things to watch out for, so please take
|
||||
a look at the section :ref:`security_considerations`.
|
||||
|
||||
Translations
|
||||
------------
|
||||
|
||||
@ -70,7 +78,7 @@ Available Solidity Integrations
|
||||
Configurable Solidty linter for Atom using Solium as a base.
|
||||
|
||||
* `Solium <https://github.com/duaraghav8/Solium/>`_
|
||||
A commandline linter for Solidity which strictly follows the rules prescribed by the `Solidity Style Guide <http://solidity.readthedocs.io/en/latest/style-guide.html>`_.
|
||||
Linter to identify and fix style and security issues in Solidity.
|
||||
|
||||
* `Solhint <https://github.com/protofire/solhint>`_
|
||||
Solidity linter that provides security, style guide and best practice rules for smart contract validation.
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
.. _installing-solidity:
|
||||
|
||||
###################
|
||||
Installing Solidity
|
||||
###################
|
||||
################################
|
||||
Installing the Solidity Compiler
|
||||
################################
|
||||
|
||||
Versioning
|
||||
==========
|
||||
@ -18,30 +18,38 @@ will use the latest release.
|
||||
Remix
|
||||
=====
|
||||
|
||||
If you just want to try Solidity for small contracts, you
|
||||
can try `Remix <https://remix.ethereum.org/>`_
|
||||
which does not need any installation. If you want to use it
|
||||
without connection to the Internet, you can go to
|
||||
https://github.com/ethereum/browser-solidity/tree/gh-pages and
|
||||
download the .ZIP file as explained on that page.
|
||||
*We recommend Remix for small contracts and for quickly learning Solidity.*
|
||||
|
||||
`Access Remix online <https://remix.ethereum.org/>`_, you don't need to install anything.
|
||||
If you want to use it without connection to the Internet, go to
|
||||
https://github.com/ethereum/browser-solidity/tree/gh-pages and download the .ZIP file as
|
||||
explained on that page.
|
||||
|
||||
Further options on this page detail installing commandline Solidity compiler software
|
||||
on your computer. Choose a commandline compiler if you are working on a larger contract
|
||||
or if you require more compilation options.
|
||||
|
||||
npm / Node.js
|
||||
=============
|
||||
|
||||
This is probably the most portable and most convenient way to install Solidity locally.
|
||||
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The
|
||||
`solcjs` program has less features than all options further down this page. Our
|
||||
`Using the compiler <using-the-compiler.html>` documentation assumes you are using
|
||||
the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will
|
||||
stop reading the documentation here and then continue to <https://github.com/ethereum/solc-js>,
|
||||
|
||||
A platform-independent JavaScript library is provided by compiling the C++ source
|
||||
into JavaScript using Emscripten. It can be used in projects directly (such as Remix).
|
||||
Note: The `solc-js <https://github.com/ethereum/solc-js>` project is derived from the C++
|
||||
`solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix).
|
||||
Please refer to the `solc-js <https://github.com/ethereum/solc-js>`_ repository for instructions.
|
||||
|
||||
It also contains a commandline tool called `solcjs`, which can be installed via npm:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
npm install -g solc
|
||||
|
||||
.. note::
|
||||
|
||||
The commandline is named `solcjs`.
|
||||
|
||||
The comandline options of `solcjs` are not compatible with `solc` and tools (such as `geth`)
|
||||
expecting the behaviour of `solc` will not work with `solcjs`.
|
||||
|
||||
@ -63,7 +71,7 @@ output directories.
|
||||
Binary Packages
|
||||
===============
|
||||
|
||||
Binary packages of Solidity available at
|
||||
Binary packages of Solidity are available at
|
||||
`solidity/releases <https://github.com/ethereum/solidity/releases>`_.
|
||||
|
||||
We also have PPAs for Ubuntu. For the latest stable version.
|
||||
|
@ -21,11 +21,11 @@ Storage
|
||||
contract SimpleStorage {
|
||||
uint storedData;
|
||||
|
||||
function set(uint x) {
|
||||
function set(uint x) public {
|
||||
storedData = x;
|
||||
}
|
||||
|
||||
function get() constant returns (uint) {
|
||||
function get() public constant returns (uint) {
|
||||
return storedData;
|
||||
}
|
||||
}
|
||||
@ -94,16 +94,16 @@ registering with username and password - all you need is an Ethereum keypair.
|
||||
|
||||
// This is the constructor whose code is
|
||||
// run only when the contract is created.
|
||||
function Coin() {
|
||||
function Coin() public {
|
||||
minter = msg.sender;
|
||||
}
|
||||
|
||||
function mint(address receiver, uint amount) {
|
||||
function mint(address receiver, uint amount) public {
|
||||
if (msg.sender != minter) return;
|
||||
balances[receiver] += amount;
|
||||
}
|
||||
|
||||
function send(address receiver, uint amount) {
|
||||
function send(address receiver, uint amount) public {
|
||||
if (balances[msg.sender] < amount) return;
|
||||
balances[msg.sender] -= amount;
|
||||
balances[receiver] += amount;
|
||||
@ -145,7 +145,7 @@ like this one. The :ref:`getter function<getter-functions>` created by the ``pub
|
||||
is a bit more complex in this case. It roughly looks like the
|
||||
following::
|
||||
|
||||
function balances(address _account) returns (uint) {
|
||||
function balances(address _account) public view returns (uint) {
|
||||
return balances[_account];
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,13 @@ It can already be used for "inline assembly" inside Solidity and
|
||||
future versions of the Solidity compiler will even use JULIA as intermediate
|
||||
language. It should also be easy to build high-level optimizer stages for JULIA.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that the flavour used for "inline assembly" does not have types
|
||||
(everything is ``u256``) and the built-in functions are identical
|
||||
to the EVM opcodes. Please resort to the inline assembly documentation
|
||||
for details.
|
||||
|
||||
The core components of JULIA are functions, blocks, variables, literals,
|
||||
for-loops, if-statements, switch-statements, expressions and assignments to variables.
|
||||
|
||||
|
@ -131,6 +131,8 @@ user interface for the contract.
|
||||
Furthermore, Mist can use the userdoc to display a confirmation message to the user
|
||||
whenever they interact with the contract.
|
||||
|
||||
Additional information about Ethereum Natural Specification (NatSpec) can be found `here <https://github.com/ethereum/wiki/wiki/Ethereum-Natural-Specification-Format>`_.
|
||||
|
||||
Usage for Source Code Verification
|
||||
==================================
|
||||
|
||||
|
@ -62,17 +62,34 @@ complete contract):
|
||||
/// Mapping of ether shares of the contract.
|
||||
mapping(address => uint) shares;
|
||||
/// Withdraw your share.
|
||||
function withdraw() {
|
||||
function withdraw() public {
|
||||
if (msg.sender.send(shares[msg.sender]))
|
||||
shares[msg.sender] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
The problem is not too serious here because of the limited gas as part
|
||||
of ``send``, but it still exposes a weakness: Ether transfer always
|
||||
includes code execution, so the recipient could be a contract that calls
|
||||
of ``send``, but it still exposes a weakness: Ether transfer can always
|
||||
include code execution, so the recipient could be a contract that calls
|
||||
back into ``withdraw``. This would let it get multiple refunds and
|
||||
basically retrieve all the Ether in the contract.
|
||||
basically retrieve all the Ether in the contract. In particular, the
|
||||
following contract will allow an attacker to refund multiple times
|
||||
as it uses ``call`` which forwards all remaining gas by default:
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
|
||||
contract Fund {
|
||||
/// Mapping of ether shares of the contract.
|
||||
mapping(address => uint) shares;
|
||||
/// Withdraw your share.
|
||||
function withdraw() public {
|
||||
if (msg.sender.call.value(shares[msg.sender])())
|
||||
shares[msg.sender] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as
|
||||
outlined further below:
|
||||
@ -85,7 +102,7 @@ outlined further below:
|
||||
/// Mapping of ether shares of the contract.
|
||||
mapping(address => uint) shares;
|
||||
/// Withdraw your share.
|
||||
function withdraw() {
|
||||
function withdraw() public {
|
||||
var share = shares[msg.sender];
|
||||
shares[msg.sender] = 0;
|
||||
msg.sender.transfer(share);
|
||||
@ -169,11 +186,11 @@ Never use tx.origin for authorization. Let's say you have a wallet contract like
|
||||
contract TxUserWallet {
|
||||
address owner;
|
||||
|
||||
function TxUserWallet() {
|
||||
function TxUserWallet() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function transferTo(address dest, uint amount) {
|
||||
function transferTo(address dest, uint amount) public {
|
||||
require(tx.origin == owner);
|
||||
dest.transfer(amount);
|
||||
}
|
||||
@ -186,17 +203,17 @@ Now someone tricks you into sending ether to the address of this attack wallet:
|
||||
pragma solidity ^0.4.11;
|
||||
|
||||
interface TxUserWallet {
|
||||
function transferTo(address dest, uint amount);
|
||||
function transferTo(address dest, uint amount) public;
|
||||
}
|
||||
|
||||
contract TxAttackWallet {
|
||||
address owner;
|
||||
|
||||
function TxAttackWallet() {
|
||||
function TxAttackWallet() public {
|
||||
owner = msg.sender;
|
||||
}
|
||||
|
||||
function() {
|
||||
function() public {
|
||||
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ of votes.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.11;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
/// @title Voting with delegation.
|
||||
contract Ballot {
|
||||
@ -66,7 +66,7 @@ of votes.
|
||||
Proposal[] public proposals;
|
||||
|
||||
/// Create a new ballot to choose one of `proposalNames`.
|
||||
function Ballot(bytes32[] proposalNames) {
|
||||
function Ballot(bytes32[] proposalNames) public {
|
||||
chairperson = msg.sender;
|
||||
voters[chairperson].weight = 1;
|
||||
|
||||
@ -86,7 +86,7 @@ of votes.
|
||||
|
||||
// Give `voter` the right to vote on this ballot.
|
||||
// May only be called by `chairperson`.
|
||||
function giveRightToVote(address voter) {
|
||||
function giveRightToVote(address voter) public {
|
||||
// If the argument of `require` evaluates to `false`,
|
||||
// it terminates and reverts all changes to
|
||||
// the state and to Ether balances. It is often
|
||||
@ -99,7 +99,7 @@ of votes.
|
||||
}
|
||||
|
||||
/// Delegate your vote to the voter `to`.
|
||||
function delegate(address to) {
|
||||
function delegate(address to) public {
|
||||
// assigns reference
|
||||
Voter storage sender = voters[msg.sender];
|
||||
require(!sender.voted);
|
||||
@ -140,7 +140,7 @@ of votes.
|
||||
|
||||
/// Give your vote (including votes delegated to you)
|
||||
/// to proposal `proposals[proposal].name`.
|
||||
function vote(uint proposal) {
|
||||
function vote(uint proposal) public {
|
||||
Voter storage sender = voters[msg.sender];
|
||||
require(!sender.voted);
|
||||
sender.voted = true;
|
||||
@ -154,7 +154,7 @@ of votes.
|
||||
|
||||
/// @dev Computes the winning proposal taking all
|
||||
/// previous votes into account.
|
||||
function winningProposal() constant
|
||||
function winningProposal() public view
|
||||
returns (uint winningProposal)
|
||||
{
|
||||
uint winningVoteCount = 0;
|
||||
@ -169,7 +169,7 @@ of votes.
|
||||
// Calls winningProposal() function to get the index
|
||||
// of the winner contained in the proposals array and then
|
||||
// returns the name of the winner
|
||||
function winnerName() constant
|
||||
function winnerName() public view
|
||||
returns (bytes32 winnerName)
|
||||
{
|
||||
winnerName = proposals[winningProposal()].name;
|
||||
@ -248,7 +248,7 @@ activate themselves.
|
||||
function SimpleAuction(
|
||||
uint _biddingTime,
|
||||
address _beneficiary
|
||||
) {
|
||||
) public {
|
||||
beneficiary = _beneficiary;
|
||||
auctionEnd = now + _biddingTime;
|
||||
}
|
||||
@ -257,7 +257,7 @@ activate themselves.
|
||||
/// together with this transaction.
|
||||
/// The value will only be refunded if the
|
||||
/// auction is not won.
|
||||
function bid() payable {
|
||||
function bid() public payable {
|
||||
// No arguments are necessary, all
|
||||
// information is already part of
|
||||
// the transaction. The keyword payable
|
||||
@ -286,7 +286,7 @@ activate themselves.
|
||||
}
|
||||
|
||||
/// Withdraw a bid that was overbid.
|
||||
function withdraw() returns (bool) {
|
||||
function withdraw() public returns (bool) {
|
||||
uint amount = pendingReturns[msg.sender];
|
||||
if (amount > 0) {
|
||||
// It is important to set this to zero because the recipient
|
||||
@ -305,7 +305,7 @@ activate themselves.
|
||||
|
||||
/// End the auction and send the highest bid
|
||||
/// to the beneficiary.
|
||||
function auctionEnd() {
|
||||
function auctionEnd() public {
|
||||
// It is a good guideline to structure functions that interact
|
||||
// with other contracts (i.e. they call functions or send Ether)
|
||||
// into three phases:
|
||||
@ -405,7 +405,7 @@ high or low invalid bids.
|
||||
uint _biddingTime,
|
||||
uint _revealTime,
|
||||
address _beneficiary
|
||||
) {
|
||||
) public {
|
||||
beneficiary = _beneficiary;
|
||||
biddingEnd = now + _biddingTime;
|
||||
revealEnd = biddingEnd + _revealTime;
|
||||
@ -421,6 +421,7 @@ high or low invalid bids.
|
||||
/// still make the required deposit. The same address can
|
||||
/// place multiple bids.
|
||||
function bid(bytes32 _blindedBid)
|
||||
public
|
||||
payable
|
||||
onlyBefore(biddingEnd)
|
||||
{
|
||||
@ -438,6 +439,7 @@ high or low invalid bids.
|
||||
bool[] _fake,
|
||||
bytes32[] _secret
|
||||
)
|
||||
public
|
||||
onlyAfter(biddingEnd)
|
||||
onlyBefore(revealEnd)
|
||||
{
|
||||
@ -487,7 +489,7 @@ high or low invalid bids.
|
||||
}
|
||||
|
||||
/// Withdraw a bid that was overbid.
|
||||
function withdraw() {
|
||||
function withdraw() public {
|
||||
uint amount = pendingReturns[msg.sender];
|
||||
if (amount > 0) {
|
||||
// It is important to set this to zero because the recipient
|
||||
@ -503,6 +505,7 @@ high or low invalid bids.
|
||||
/// End the auction and send the highest bid
|
||||
/// to the beneficiary.
|
||||
function auctionEnd()
|
||||
public
|
||||
onlyAfter(revealEnd)
|
||||
{
|
||||
require(!ended);
|
||||
@ -533,7 +536,7 @@ Safe Remote Purchase
|
||||
// Ensure that `msg.value` is an even number.
|
||||
// Division will truncate if it is an odd number.
|
||||
// Check via multiplication that it wasn't an odd number.
|
||||
function Purchase() payable {
|
||||
function Purchase() public payable {
|
||||
seller = msg.sender;
|
||||
value = msg.value / 2;
|
||||
require((2 * value) == msg.value);
|
||||
@ -567,6 +570,7 @@ Safe Remote Purchase
|
||||
/// Can only be called by the seller before
|
||||
/// the contract is locked.
|
||||
function abort()
|
||||
public
|
||||
onlySeller
|
||||
inState(State.Created)
|
||||
{
|
||||
@ -580,6 +584,7 @@ Safe Remote Purchase
|
||||
/// The ether will be locked until confirmReceived
|
||||
/// is called.
|
||||
function confirmPurchase()
|
||||
public
|
||||
inState(State.Created)
|
||||
condition(msg.value == (2 * value))
|
||||
payable
|
||||
@ -592,6 +597,7 @@ Safe Remote Purchase
|
||||
/// Confirm that you (the buyer) received the item.
|
||||
/// This will release the locked ether.
|
||||
function confirmReceived()
|
||||
public
|
||||
onlyBuyer
|
||||
inState(State.Locked)
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ Structure of a Contract
|
||||
|
||||
Contracts in Solidity are similar to classes in object-oriented languages.
|
||||
Each contract can contain declarations of :ref:`structure-state-variables`, :ref:`structure-functions`,
|
||||
:ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-structs-types` and :ref:`structure-enum-types`.
|
||||
:ref:`structure-function-modifiers`, :ref:`structure-events`, :ref:`structure-struct-types` and :ref:`structure-enum-types`.
|
||||
Furthermore, contracts can inherit from other contracts.
|
||||
|
||||
.. _structure-state-variables:
|
||||
@ -43,7 +43,7 @@ Functions are the executable units of code within a contract.
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract SimpleAuction {
|
||||
function bid() payable { // Function
|
||||
function bid() public payable { // Function
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@ -72,7 +72,7 @@ Function modifiers can be used to amend the semantics of functions in a declarat
|
||||
_;
|
||||
}
|
||||
|
||||
function abort() onlySeller { // Modifier usage
|
||||
function abort() public onlySeller { // Modifier usage
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@ -91,7 +91,7 @@ Events are convenience interfaces with the EVM logging facilities.
|
||||
contract SimpleAuction {
|
||||
event HighestBidIncreased(address bidder, uint amount); // Event
|
||||
|
||||
function bid() payable {
|
||||
function bid() public payable {
|
||||
// ...
|
||||
HighestBidIncreased(msg.sender, msg.value); // Triggering event
|
||||
}
|
||||
@ -100,9 +100,9 @@ Events are convenience interfaces with the EVM logging facilities.
|
||||
See :ref:`events` in contracts section for information on how events are declared
|
||||
and can be used from within a dapp.
|
||||
|
||||
.. _structure-structs-types:
|
||||
.. _structure-struct-types:
|
||||
|
||||
Structs Types
|
||||
Struct Types
|
||||
=============
|
||||
|
||||
Structs are custom defined types that can group several variables (see
|
||||
|
@ -86,17 +86,17 @@ Blank lines may be omitted between groups of related one-liners (such as stub fu
|
||||
Yes::
|
||||
|
||||
contract A {
|
||||
function spam();
|
||||
function ham();
|
||||
function spam() public;
|
||||
function ham() public;
|
||||
}
|
||||
|
||||
|
||||
contract B is A {
|
||||
function spam() {
|
||||
function spam() public {
|
||||
...
|
||||
}
|
||||
|
||||
function ham() {
|
||||
function ham() public {
|
||||
...
|
||||
}
|
||||
}
|
||||
@ -104,10 +104,10 @@ Yes::
|
||||
No::
|
||||
|
||||
contract A {
|
||||
function spam() {
|
||||
function spam() public {
|
||||
...
|
||||
}
|
||||
function ham() {
|
||||
function ham() public {
|
||||
...
|
||||
}
|
||||
}
|
||||
@ -169,11 +169,11 @@ Within a grouping, place the ``constant`` functions last.
|
||||
Yes::
|
||||
|
||||
contract A {
|
||||
function A() {
|
||||
function A() public {
|
||||
...
|
||||
}
|
||||
|
||||
function() {
|
||||
function() public {
|
||||
...
|
||||
}
|
||||
|
||||
@ -206,11 +206,11 @@ No::
|
||||
// Public functions
|
||||
// ...
|
||||
|
||||
function A() {
|
||||
function A() public {
|
||||
...
|
||||
}
|
||||
|
||||
function() {
|
||||
function() public {
|
||||
...
|
||||
}
|
||||
|
||||
@ -235,17 +235,17 @@ No::
|
||||
|
||||
Exception::
|
||||
|
||||
function singleLine() { spam(); }
|
||||
function singleLine() public { spam(); }
|
||||
|
||||
Immediately before a comma, semicolon:
|
||||
|
||||
Yes::
|
||||
|
||||
function spam(uint i, Coin coin);
|
||||
function spam(uint i, Coin coin) public;
|
||||
|
||||
No::
|
||||
|
||||
function spam(uint i , Coin coin) ;
|
||||
function spam(uint i , Coin coin) public ;
|
||||
|
||||
More than one space around an assignment or other operator to align with
|
||||
another:
|
||||
@ -266,13 +266,13 @@ Don't include a whitespace in the fallback function:
|
||||
|
||||
Yes::
|
||||
|
||||
function() {
|
||||
function() public {
|
||||
...
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
function () {
|
||||
function () public {
|
||||
...
|
||||
}
|
||||
|
||||
@ -395,30 +395,30 @@ The opening brace should be preceeded by a single space.
|
||||
|
||||
Yes::
|
||||
|
||||
function increment(uint x) returns (uint) {
|
||||
function increment(uint x) public pure returns (uint) {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
function increment(uint x) public onlyowner returns (uint) {
|
||||
function increment(uint x) public pure onlyowner returns (uint) {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
function increment(uint x) returns (uint)
|
||||
function increment(uint x) public pure returns (uint)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
function increment(uint x) returns (uint){
|
||||
function increment(uint x) public pure returns (uint){
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
function increment(uint x) returns (uint) {
|
||||
function increment(uint x) public pure returns (uint) {
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
function increment(uint x) returns (uint) {
|
||||
function increment(uint x) public pure returns (uint) {
|
||||
return x + 1;}
|
||||
|
||||
The visibility modifiers for a function should come before any custom
|
||||
@ -450,14 +450,16 @@ Yes::
|
||||
address d,
|
||||
address e,
|
||||
address f
|
||||
) {
|
||||
)
|
||||
public
|
||||
{
|
||||
doSomething();
|
||||
}
|
||||
|
||||
No::
|
||||
|
||||
function thisFunctionHasLotsOfArguments(address a, address b, address c,
|
||||
address d, address e, address f) {
|
||||
address d, address e, address f) public {
|
||||
doSomething();
|
||||
}
|
||||
|
||||
@ -466,7 +468,7 @@ No::
|
||||
address c,
|
||||
address d,
|
||||
address e,
|
||||
address f) {
|
||||
address f) public {
|
||||
doSomething();
|
||||
}
|
||||
|
||||
@ -476,12 +478,12 @@ No::
|
||||
address c,
|
||||
address d,
|
||||
address e,
|
||||
address f) {
|
||||
address f) public {
|
||||
doSomething();
|
||||
}
|
||||
|
||||
If a long function declaration has modifiers, then each modifier should be
|
||||
dropped to it's own line.
|
||||
dropped to its own line.
|
||||
|
||||
Yes::
|
||||
|
||||
@ -542,6 +544,7 @@ Yes::
|
||||
B(param1)
|
||||
C(param2, param3)
|
||||
D(param4)
|
||||
public
|
||||
{
|
||||
// do something with param5
|
||||
}
|
||||
@ -554,6 +557,7 @@ No::
|
||||
B(param1)
|
||||
C(param2, param3)
|
||||
D(param4)
|
||||
public
|
||||
{
|
||||
// do something with param5
|
||||
}
|
||||
@ -563,7 +567,8 @@ No::
|
||||
function A(uint param1, uint param2, uint param3, uint param4, uint param5)
|
||||
B(param1)
|
||||
C(param2, param3)
|
||||
D(param4) {
|
||||
D(param4)
|
||||
public {
|
||||
// do something with param5
|
||||
}
|
||||
}
|
||||
@ -572,7 +577,7 @@ When declaring short functions with a single statement, it is permissible to do
|
||||
|
||||
Permissible::
|
||||
|
||||
function shortFunction() { doSomething(); }
|
||||
function shortFunction() public { doSomething(); }
|
||||
|
||||
These guidelines for function declarations are intended to improve readability.
|
||||
Authors should use their best judgement as this guide does not try to cover all
|
||||
@ -699,6 +704,12 @@ Contract and Library Names
|
||||
Contracts and libraries should be named using the CapWords style. Examples: ``SimpleToken``, ``SmartBank``, ``CertificateHashRepository``, ``Player``.
|
||||
|
||||
|
||||
Struct Names
|
||||
==========================
|
||||
|
||||
Structs should be named using the CapWords style. Examples: ``MyCoin``, ``Position``, ``PositionXY``.
|
||||
|
||||
|
||||
Event Names
|
||||
===========
|
||||
|
||||
@ -708,7 +719,7 @@ Events should be named using the CapWords style. Examples: ``Deposit``, ``Transf
|
||||
Function Names
|
||||
==============
|
||||
|
||||
Functions should use mixedCase. Examples: ``getBalance``, ``transfer``, ``verifyOwner``, ``addMember``, ``changeOwner``.
|
||||
Functions other than constructors should use mixedCase. Examples: ``getBalance``, ``transfer``, ``verifyOwner``, ``addMember``, ``changeOwner``.
|
||||
|
||||
|
||||
Function Argument Names
|
||||
@ -739,6 +750,12 @@ Modifier Names
|
||||
Use mixedCase. Examples: ``onlyBy``, ``onlyAfter``, ``onlyDuringThePreSale``.
|
||||
|
||||
|
||||
Enums
|
||||
=====
|
||||
|
||||
Enums, in the style of simple type declarations, should be named using the CapWords style. Examples: ``TokenGroup``, ``Frame``, ``HashStyle``, ``CharacterLocation``.
|
||||
|
||||
|
||||
Avoiding Naming Collisions
|
||||
==========================
|
||||
|
||||
|
@ -79,8 +79,8 @@ Fixed Point Numbers
|
||||
Fixed point numbers are not fully supported by Solidity yet. They can be declared, but
|
||||
cannot be assigned to or from.
|
||||
|
||||
``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represent the number of bits taken by
|
||||
the type and ``N`` represent how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive.
|
||||
``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by
|
||||
the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive.
|
||||
``ufixed`` and ``fixed`` are aliases for ``ufixed128x19`` and ``fixed128x19``, respectively.
|
||||
|
||||
Operators:
|
||||
@ -331,14 +331,14 @@ check the value ranges at runtime and a failure causes an exception. Enums need
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract test {
|
||||
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
|
||||
ActionChoices choice;
|
||||
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
|
||||
|
||||
function setGoStraight() {
|
||||
function setGoStraight() public {
|
||||
choice = ActionChoices.GoStraight;
|
||||
}
|
||||
|
||||
@ -347,11 +347,11 @@ check the value ranges at runtime and a failure causes an exception. Enums need
|
||||
// for all matters external to Solidity. The integer type used is just
|
||||
// large enough to hold all enum values, i.e. if you have more values,
|
||||
// `uint16` will be used and so on.
|
||||
function getChoice() returns (ActionChoices) {
|
||||
function getChoice() public view returns (ActionChoices) {
|
||||
return choice;
|
||||
}
|
||||
|
||||
function getDefaultChoice() returns (uint) {
|
||||
function getDefaultChoice() public pure returns (uint) {
|
||||
return uint(defaultChoice);
|
||||
}
|
||||
}
|
||||
@ -409,23 +409,24 @@ just use ``f``, if you want to use its external form, use ``this.f``.
|
||||
Additionally, public (or external) functions also have a special member called ``selector``,
|
||||
which returns the :ref:`ABI function selector <abi_function_selector>`::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract Selector {
|
||||
function f() returns (bytes4) {
|
||||
function f() public view returns (bytes4) {
|
||||
return this.f.selector;
|
||||
}
|
||||
}
|
||||
|
||||
Example that shows how to use internal function types::
|
||||
|
||||
pragma solidity ^0.4.5;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
library ArrayUtils {
|
||||
// internal functions can be used in internal library functions because
|
||||
// they will be part of the same code context
|
||||
function map(uint[] memory self, function (uint) returns (uint) f)
|
||||
function map(uint[] memory self, function (uint) pure returns (uint) f)
|
||||
internal
|
||||
pure
|
||||
returns (uint[] memory r)
|
||||
{
|
||||
r = new uint[](self.length);
|
||||
@ -435,9 +436,10 @@ Example that shows how to use internal function types::
|
||||
}
|
||||
function reduce(
|
||||
uint[] memory self,
|
||||
function (uint, uint) returns (uint) f
|
||||
function (uint, uint) pure returns (uint) f
|
||||
)
|
||||
internal
|
||||
pure
|
||||
returns (uint r)
|
||||
{
|
||||
r = self[0];
|
||||
@ -445,7 +447,7 @@ Example that shows how to use internal function types::
|
||||
r = f(r, self[i]);
|
||||
}
|
||||
}
|
||||
function range(uint length) internal returns (uint[] memory r) {
|
||||
function range(uint length) internal pure returns (uint[] memory r) {
|
||||
r = new uint[](length);
|
||||
for (uint i = 0; i < r.length; i++) {
|
||||
r[i] = i;
|
||||
@ -455,13 +457,13 @@ Example that shows how to use internal function types::
|
||||
|
||||
contract Pyramid {
|
||||
using ArrayUtils for *;
|
||||
function pyramid(uint l) returns (uint) {
|
||||
function pyramid(uint l) public pure returns (uint) {
|
||||
return ArrayUtils.range(l).map(square).reduce(sum);
|
||||
}
|
||||
function square(uint x) internal returns (uint) {
|
||||
function square(uint x) internal pure returns (uint) {
|
||||
return x * x;
|
||||
}
|
||||
function sum(uint x, uint y) internal returns (uint) {
|
||||
function sum(uint x, uint y) internal pure returns (uint) {
|
||||
return x + y;
|
||||
}
|
||||
}
|
||||
@ -477,11 +479,11 @@ Another example that uses external function types::
|
||||
}
|
||||
Request[] requests;
|
||||
event NewRequest(uint);
|
||||
function query(bytes data, function(bytes memory) external callback) {
|
||||
function query(bytes data, function(bytes memory) external callback) public {
|
||||
requests.push(Request(data, callback));
|
||||
NewRequest(requests.length - 1);
|
||||
}
|
||||
function reply(uint requestID, bytes response) {
|
||||
function reply(uint requestID, bytes response) public {
|
||||
// Here goes the check that the reply comes from a trusted source
|
||||
requests[requestID].callback(response);
|
||||
}
|
||||
@ -492,7 +494,7 @@ Another example that uses external function types::
|
||||
function buySomething() {
|
||||
oracle.query("USD", this.oracleResponse);
|
||||
}
|
||||
function oracleResponse(bytes response) {
|
||||
function oracleResponse(bytes response) public {
|
||||
require(msg.sender == address(oracle));
|
||||
// Use the data
|
||||
}
|
||||
@ -543,7 +545,7 @@ memory-stored reference type do not create a copy.
|
||||
uint[] x; // the data location of x is storage
|
||||
|
||||
// the data location of memoryArray is memory
|
||||
function f(uint[] memoryArray) {
|
||||
function f(uint[] memoryArray) public {
|
||||
x = memoryArray; // works, copies the whole array to storage
|
||||
var y = x; // works, assigns a pointer, data location of y is storage
|
||||
y[7]; // fine, returns the 8th element
|
||||
@ -560,7 +562,7 @@ memory-stored reference type do not create a copy.
|
||||
}
|
||||
|
||||
function g(uint[] storage storageArray) internal {}
|
||||
function h(uint[] memoryArray) {}
|
||||
function h(uint[] memoryArray) public {}
|
||||
}
|
||||
|
||||
Summary
|
||||
@ -620,10 +622,10 @@ the ``.length`` member.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f(uint len) {
|
||||
function f(uint len) public pure {
|
||||
uint[] memory a = new uint[](7);
|
||||
bytes memory b = new bytes(len);
|
||||
// Here we have a.length == 7 and b.length == len
|
||||
@ -641,13 +643,13 @@ assigned to a variable right away.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract C {
|
||||
function f() {
|
||||
function f() public pure {
|
||||
g([uint(1), 2, 3]);
|
||||
}
|
||||
function g(uint[3] _data) {
|
||||
function g(uint[3] _data) public pure {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@ -667,7 +669,7 @@ possible:
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function f() {
|
||||
function f() public {
|
||||
// The next line creates a type error because uint[3] memory
|
||||
// cannot be converted to uint[] memory.
|
||||
uint[] x = [uint(1), 3, 4];
|
||||
@ -703,7 +705,7 @@ Members
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
pragma solidity ^0.4.16;
|
||||
|
||||
contract ArrayContract {
|
||||
uint[2**20] m_aLotOfIntegers;
|
||||
@ -712,23 +714,23 @@ Members
|
||||
bool[2][] m_pairsOfFlags;
|
||||
// newPairs is stored in memory - the default for function arguments
|
||||
|
||||
function setAllFlagPairs(bool[2][] newPairs) {
|
||||
function setAllFlagPairs(bool[2][] newPairs) public {
|
||||
// assignment to a storage array replaces the complete array
|
||||
m_pairsOfFlags = newPairs;
|
||||
}
|
||||
|
||||
function setFlagPair(uint index, bool flagA, bool flagB) {
|
||||
function setFlagPair(uint index, bool flagA, bool flagB) public {
|
||||
// access to a non-existing index will throw an exception
|
||||
m_pairsOfFlags[index][0] = flagA;
|
||||
m_pairsOfFlags[index][1] = flagB;
|
||||
}
|
||||
|
||||
function changeFlagArraySize(uint newSize) {
|
||||
function changeFlagArraySize(uint newSize) public {
|
||||
// if the new size is smaller, removed array elements will be cleared
|
||||
m_pairsOfFlags.length = newSize;
|
||||
}
|
||||
|
||||
function clear() {
|
||||
function clear() public {
|
||||
// these clear the arrays completely
|
||||
delete m_pairsOfFlags;
|
||||
delete m_aLotOfIntegers;
|
||||
@ -738,20 +740,20 @@ Members
|
||||
|
||||
bytes m_byteData;
|
||||
|
||||
function byteArrays(bytes data) {
|
||||
function byteArrays(bytes data) public {
|
||||
// byte arrays ("bytes") are different as they are stored without padding,
|
||||
// but can be treated identical to "uint8[]"
|
||||
m_byteData = data;
|
||||
m_byteData.length += 7;
|
||||
m_byteData[3] = 8;
|
||||
m_byteData[3] = byte(8);
|
||||
delete m_byteData[2];
|
||||
}
|
||||
|
||||
function addFlag(bool[2] flag) returns (uint) {
|
||||
function addFlag(bool[2] flag) public returns (uint) {
|
||||
return m_pairsOfFlags.push(flag);
|
||||
}
|
||||
|
||||
function createMemoryArray(uint size) returns (bytes) {
|
||||
function createMemoryArray(uint size) public pure returns (bytes) {
|
||||
// Dynamic memory arrays are created using `new`:
|
||||
uint[2][] memory arrayOfPairs = new uint[2][](size);
|
||||
// Create a dynamic byte array:
|
||||
@ -795,13 +797,13 @@ shown in the following example:
|
||||
uint numCampaigns;
|
||||
mapping (uint => Campaign) campaigns;
|
||||
|
||||
function newCampaign(address beneficiary, uint goal) returns (uint campaignID) {
|
||||
function newCampaign(address beneficiary, uint goal) public returns (uint campaignID) {
|
||||
campaignID = numCampaigns++; // campaignID is return variable
|
||||
// Creates new struct and saves in storage. We leave out the mapping type.
|
||||
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
|
||||
}
|
||||
|
||||
function contribute(uint campaignID) payable {
|
||||
function contribute(uint campaignID) public payable {
|
||||
Campaign storage c = campaigns[campaignID];
|
||||
// Creates a new temporary memory struct, initialised with the given values
|
||||
// and copies it over to storage.
|
||||
@ -810,7 +812,7 @@ shown in the following example:
|
||||
c.amount += msg.value;
|
||||
}
|
||||
|
||||
function checkGoalReached(uint campaignID) returns (bool reached) {
|
||||
function checkGoalReached(uint campaignID) public returns (bool reached) {
|
||||
Campaign storage c = campaigns[campaignID];
|
||||
if (c.amount < c.fundingGoal)
|
||||
return false;
|
||||
@ -872,13 +874,13 @@ for each ``_KeyType``, recursively.
|
||||
contract MappingExample {
|
||||
mapping(address => uint) public balances;
|
||||
|
||||
function update(uint newBalance) {
|
||||
function update(uint newBalance) public {
|
||||
balances[msg.sender] = newBalance;
|
||||
}
|
||||
}
|
||||
|
||||
contract MappingUser {
|
||||
function f() returns (uint) {
|
||||
function f() public returns (uint) {
|
||||
MappingExample m = new MappingExample();
|
||||
m.update(100);
|
||||
return m.balances(this);
|
||||
@ -897,7 +899,7 @@ Operators Involving LValues
|
||||
|
||||
If ``a`` is an LValue (i.e. a variable or something that can be assigned to), the following operators are available as shorthands:
|
||||
|
||||
``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, ``a |=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change.
|
||||
``a += e`` is equivalent to ``a = a + e``. The operators ``-=``, ``*=``, ``/=``, ``%=``, ``|=``, ``&=`` and ``^=`` are defined accordingly. ``a++`` and ``a--`` are equivalent to ``a += 1`` / ``a -= 1`` but the expression itself still has the previous value of ``a``. In contrast, ``--a`` and ``++a`` have the same effect on ``a`` but return the value after the change.
|
||||
|
||||
delete
|
||||
------
|
||||
@ -916,11 +918,11 @@ It is important to note that ``delete a`` really behaves like an assignment to `
|
||||
uint data;
|
||||
uint[] dataArray;
|
||||
|
||||
function f() {
|
||||
function f() public {
|
||||
uint x = data;
|
||||
delete x; // sets x to 0, does not affect data
|
||||
delete data; // sets data to 0, does not affect x which still holds a copy
|
||||
uint[] y = dataArray;
|
||||
uint[] storage y = dataArray;
|
||||
delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
|
||||
// y is affected which is an alias to the storage object
|
||||
// On the other hand: "delete y" is not valid, as assignments to local variables
|
||||
|
@ -34,7 +34,7 @@ library has to be updated by an external oracle.
|
||||
These suffixes cannot be applied to variables. If you want to
|
||||
interpret some input variable in e.g. days, you can do it in the following way::
|
||||
|
||||
function f(uint start, uint daysAfter) {
|
||||
function f(uint start, uint daysAfter) public {
|
||||
if (now >= start + daysAfter * 1 days) {
|
||||
// ...
|
||||
}
|
||||
|
@ -147,11 +147,11 @@ Input Description
|
||||
},
|
||||
// Enable the abi and opcodes output of MyContract defined in file def.
|
||||
"def": {
|
||||
"MyContract": [ "abi", "evm.opcodes" ]
|
||||
"MyContract": [ "abi", "evm.bytecode.opcodes" ]
|
||||
},
|
||||
// Enable the source map output of every single contract.
|
||||
"*": {
|
||||
"*": [ "evm.sourceMap" ]
|
||||
"*": [ "evm.bytecode.sourceMap" ]
|
||||
},
|
||||
// Enable the legacy AST output of every single file.
|
||||
"*": {
|
||||
@ -177,7 +177,8 @@ Output Description
|
||||
start: 0,
|
||||
end: 100
|
||||
],
|
||||
// Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc
|
||||
// Mandatory: Error type, such as "TypeError", "InternalCompilerError", "Exception", etc.
|
||||
// See below for complete list of types.
|
||||
type: "TypeError",
|
||||
// Mandatory: Component where the error originated, such as "general", "ewasm", etc.
|
||||
component: "general",
|
||||
@ -273,3 +274,21 @@ Output Description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Error types
|
||||
~~~~~~~~~~~
|
||||
|
||||
1. ``JSONError``: JSON input doesn't conform to the required format, e.g. input is not a JSON object, the language is not supported, etc.
|
||||
2. ``IOError``: IO and import processing errors, such as unresolvable URL or hash mismatch in supplied sources.
|
||||
3. ``ParserError``: Source code doesn't conform to the language rules.
|
||||
4. ``DocstringParsingError``: The NatSpec tags in the comment block cannot be parsed.
|
||||
5. ``SyntaxError``: Syntactical error, such as ``continue`` is used outside of a ``for`` loop.
|
||||
6. ``DeclarationError``: Invalid, unresolvable or clashing identifier names. e.g. ``Identifier not found``
|
||||
7. ``TypeError``: Error within the type system, such as invalid type conversions, invalid assignments, etc.
|
||||
8. ``UnimplementedFeatureError``: Feature is not supported by the compiler, but is expected to be supported in future versions.
|
||||
9. ``InternalCompilerError``: Internal bug triggered in the compiler - this should be reported as an issue.
|
||||
10. ``Exception``: Unknown failure during compilation - this should be reported as an issue.
|
||||
11. ``CompilerError``: Invalid use of the compiler stack - this should be reported as an issue.
|
||||
12. ``FatalError``: Fatal error not processed correctly - this should be reported as an issue.
|
||||
13. ``Warning``: A warning, which didn't stop the compilation, but should be addressed if possible.
|
||||
|
@ -155,6 +155,14 @@ inline std::string formatNumber(bigint const& _value)
|
||||
return _value.str();
|
||||
}
|
||||
|
||||
inline std::string formatNumber(u256 const& _value)
|
||||
{
|
||||
if (_value > 0x1000000)
|
||||
return toHex(toCompactBigEndian(_value), 2, HexPrefix::Add);
|
||||
else
|
||||
return _value.str();
|
||||
}
|
||||
|
||||
inline std::string toCompactHexWithPrefix(u256 val)
|
||||
{
|
||||
std::ostringstream ret;
|
||||
@ -183,6 +191,12 @@ template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U con
|
||||
_a.push_back(i);
|
||||
return _a;
|
||||
}
|
||||
/// Concatenate the contents of a container onto a vector, move variant.
|
||||
template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U&& _b)
|
||||
{
|
||||
std::move(_b.begin(), _b.end(), std::back_inserter(_a));
|
||||
return _a;
|
||||
}
|
||||
/// Concatenate the contents of a container onto a set
|
||||
template <class T, class U> std::set<T>& operator+=(std::set<T>& _a, U const& _b)
|
||||
{
|
||||
@ -197,6 +211,17 @@ inline std::vector<T> operator+(std::vector<T> const& _a, std::vector<T> const&
|
||||
ret += _b;
|
||||
return ret;
|
||||
}
|
||||
/// Concatenate two vectors of elements, moving them.
|
||||
template <class T>
|
||||
inline std::vector<T> operator+(std::vector<T>&& _a, std::vector<T>&& _b)
|
||||
{
|
||||
std::vector<T> ret(std::move(_a));
|
||||
if (&_a == &_b)
|
||||
ret += ret;
|
||||
else
|
||||
ret += std::move(_b);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T, class V>
|
||||
bool contains(T const& _t, V const& _v)
|
||||
|
101
libdevcore/StringUtils.cpp
Normal file
101
libdevcore/StringUtils.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file StringUtils.h
|
||||
* @author Balajiganapathi S <balajiganapathi.s@gmail.com>
|
||||
* @date 2017
|
||||
*
|
||||
* String routines
|
||||
*/
|
||||
|
||||
#include "StringUtils.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
|
||||
bool dev::stringWithinDistance(string const& _str1, string const& _str2, size_t _maxDistance)
|
||||
{
|
||||
if (_str1 == _str2)
|
||||
return true;
|
||||
|
||||
size_t n1 = _str1.size();
|
||||
size_t n2 = _str2.size();
|
||||
size_t distance = stringDistance(_str1, _str2);
|
||||
|
||||
// if distance is not greater than _maxDistance, and distance is strictly less than length of both names, they can be considered similar
|
||||
// this is to avoid irrelevant suggestions
|
||||
return distance <= _maxDistance && distance < n1 && distance < n2;
|
||||
}
|
||||
|
||||
size_t dev::stringDistance(string const& _str1, string const& _str2)
|
||||
{
|
||||
size_t n1 = _str1.size();
|
||||
size_t n2 = _str2.size();
|
||||
// Optimize by storing only last 2 rows and current row. So first index is considered modulo 3
|
||||
// This is a two-dimensional array of size 3 x (n2 + 1).
|
||||
vector<size_t> dp(3 * (n2 + 1));
|
||||
|
||||
// In this dp formulation of Damerau–Levenshtein distance we are assuming that the strings are 1-based to make base case storage easier.
|
||||
// So index accesser to _name1 and _name2 have to be adjusted accordingly
|
||||
for (size_t i1 = 0; i1 <= n1; ++i1)
|
||||
for (size_t i2 = 0; i2 <= n2; ++i2)
|
||||
{
|
||||
size_t x = 0;
|
||||
if (min(i1, i2) == 0) // base case
|
||||
x = max(i1, i2);
|
||||
else
|
||||
{
|
||||
size_t left = dp[(i1 - 1) % 3 + i2 * 3];
|
||||
size_t up = dp[(i1 % 3) + (i2 - 1) * 3];
|
||||
size_t upleft = dp[((i1 - 1) % 3) + (i2 - 1) * 3];
|
||||
// deletion and insertion
|
||||
x = min(left + 1, up + 1);
|
||||
if (_str1[i1-1] == _str2[i2-1])
|
||||
// same chars, can skip
|
||||
x = min(x, upleft);
|
||||
else
|
||||
// different chars so try substitution
|
||||
x = min(x, upleft + 1);
|
||||
|
||||
// transposing
|
||||
if (i1 > 1 && i2 > 1 && _str1[i1 - 1] == _str2[i2 - 2] && _str1[i1 - 2] == _str2[i2 - 1])
|
||||
x = min(x, dp[((i1 - 2) % 3) + (i2 - 2) * 3] + 1);
|
||||
}
|
||||
dp[(i1 % 3) + i2 * 3] = x;
|
||||
}
|
||||
|
||||
return dp[(n1 % 3) + n2 * 3];
|
||||
}
|
||||
|
||||
string dev::quotedAlternativesList(vector<string> const& suggestions)
|
||||
{
|
||||
if (suggestions.empty())
|
||||
return "";
|
||||
if (suggestions.size() == 1)
|
||||
return "\"" + suggestions.front() + "\"";
|
||||
|
||||
string choices = "\"" + suggestions.front() + "\"";
|
||||
for (size_t i = 1; i + 1 < suggestions.size(); ++i)
|
||||
choices += ", \"" + suggestions[i] + "\"";
|
||||
|
||||
choices += " or \"" + suggestions.back() + "\"";
|
||||
|
||||
return choices;
|
||||
}
|
||||
|
39
libdevcore/StringUtils.h
Normal file
39
libdevcore/StringUtils.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/** @file StringUtils.h
|
||||
* @author Balajiganapathi S <balajiganapathi.s@gmail.com>
|
||||
* @date 2017
|
||||
*
|
||||
* String routines
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
|
||||
// Calculates the Damerau–Levenshtein distance between _str1 and _str2 and returns true if that distance is not greater than _maxDistance
|
||||
bool stringWithinDistance(std::string const& _str1, std::string const& _str2, size_t _maxDistance);
|
||||
// Calculates the Damerau–Levenshtein distance between _str1 and _str2
|
||||
size_t stringDistance(std::string const& _str1, std::string const& _str2);
|
||||
// Return a string having elements of suggestions as quoted, alternative suggestions. e.g. "a", "b" or "c"
|
||||
std::string quotedAlternativesList(std::vector<std::string> const& suggestions);
|
||||
|
||||
}
|
@ -283,6 +283,11 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
|
||||
createJsonValue("PUSHLIB", i.location().start, i.location().end, m_libraries.at(h256(i.data())))
|
||||
);
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
collection.append(
|
||||
createJsonValue("PUSHDEPLOYADDRESS", i.location().start, i.location().end)
|
||||
);
|
||||
break;
|
||||
case Tag:
|
||||
collection.append(
|
||||
createJsonValue("tag", i.location().start, i.location().end, string(i.data())));
|
||||
@ -590,6 +595,10 @@ LinkerObject const& Assembly::assemble() const
|
||||
ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data());
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
ret.bytecode.push_back(byte(Instruction::PUSH20));
|
||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||
break;
|
||||
case Tag:
|
||||
assertThrow(i.data() != 0, AssemblyException, "Invalid tag position.");
|
||||
assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag.");
|
||||
|
@ -68,6 +68,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
|
||||
case PushSub:
|
||||
return 1 + _addressLength;
|
||||
case PushLibraryAddress:
|
||||
case PushDeployTimeAddress:
|
||||
return 1 + 20;
|
||||
default:
|
||||
break;
|
||||
@ -97,6 +98,7 @@ int AssemblyItem::returnValues() const
|
||||
case PushSubSize:
|
||||
case PushProgramSize:
|
||||
case PushLibraryAddress:
|
||||
case PushDeployTimeAddress:
|
||||
return 1;
|
||||
case Tag:
|
||||
return 0;
|
||||
@ -119,6 +121,7 @@ bool AssemblyItem::canBeFunctional() const
|
||||
case PushSubSize:
|
||||
case PushProgramSize:
|
||||
case PushLibraryAddress:
|
||||
case PushDeployTimeAddress:
|
||||
return true;
|
||||
case Tag:
|
||||
return false;
|
||||
@ -190,6 +193,9 @@ string AssemblyItem::toAssemblyText() const
|
||||
case PushLibraryAddress:
|
||||
text = string("linkerSymbol(\"") + toHex(data()) + string("\")");
|
||||
break;
|
||||
case PushDeployTimeAddress:
|
||||
text = string("deployTimeAddress()");
|
||||
break;
|
||||
case UndefinedItem:
|
||||
assertThrow(false, AssemblyException, "Invalid assembly item.");
|
||||
break;
|
||||
@ -252,6 +258,9 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
|
||||
_out << " PushLibraryAddress " << hash.substr(0, 8) + "..." + hash.substr(hash.length() - 8);
|
||||
break;
|
||||
}
|
||||
case PushDeployTimeAddress:
|
||||
_out << " PushDeployTimeAddress";
|
||||
break;
|
||||
case UndefinedItem:
|
||||
_out << " ???";
|
||||
break;
|
||||
|
@ -46,7 +46,8 @@ enum AssemblyItemType {
|
||||
PushProgramSize,
|
||||
Tag,
|
||||
PushData,
|
||||
PushLibraryAddress ///< Push a currently unknown address of another (library) contract.
|
||||
PushLibraryAddress, ///< Push a currently unknown address of another (library) contract.
|
||||
PushDeployTimeAddress ///< Push an address to be filled at deploy time. Should not be touched by the optimizer.
|
||||
};
|
||||
|
||||
class Assembly;
|
||||
|
@ -181,7 +181,7 @@ string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const
|
||||
return str.str();
|
||||
}
|
||||
|
||||
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr, bool _secondRun)
|
||||
ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr)
|
||||
{
|
||||
static Rules rules;
|
||||
|
||||
@ -202,14 +202,7 @@ ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr,
|
||||
//cout << "with rule " << match->first.toString() << endl;
|
||||
//ExpressionTemplate t(match->second());
|
||||
//cout << "to " << match->second().toString() << endl;
|
||||
return rebuildExpression(ExpressionTemplate(match->second(), _expr.item->location()));
|
||||
}
|
||||
|
||||
if (!_secondRun && _expr.arguments.size() == 2 && SemanticInformation::isCommutativeOperation(*_expr.item))
|
||||
{
|
||||
Expression expr = _expr;
|
||||
swap(expr.arguments[0], expr.arguments[1]);
|
||||
return tryToSimplify(expr, true);
|
||||
return rebuildExpression(ExpressionTemplate(match->action(), _expr.item->location()));
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -108,8 +108,7 @@ public:
|
||||
private:
|
||||
/// Tries to simplify the given expression.
|
||||
/// @returns its class if it possible or Id(-1) otherwise.
|
||||
/// @param _secondRun is set to true for the second run where arguments of commutative expressions are reversed
|
||||
Id tryToSimplify(Expression const& _expr, bool _secondRun = false);
|
||||
Id tryToSimplify(Expression const& _expr);
|
||||
|
||||
/// Rebuilds an expression from a (matched) pattern.
|
||||
Id rebuildExpression(ExpressionTemplate const& _template);
|
||||
|
@ -52,6 +52,7 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
|
||||
case PushSubSize:
|
||||
case PushProgramSize:
|
||||
case PushLibraryAddress:
|
||||
case PushDeployTimeAddress:
|
||||
gas = runGas(Instruction::PUSH1);
|
||||
break;
|
||||
case Tag:
|
||||
|
266
libevmasm/RuleList.h
Normal file
266
libevmasm/RuleList.h
Normal file
@ -0,0 +1,266 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @date 2018
|
||||
* Templatized list of simplification rules.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <libevmasm/Instruction.h>
|
||||
#include <libevmasm/SimplificationRule.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
template <class S> S divWorkaround(S const& _a, S const& _b)
|
||||
{
|
||||
return (S)(bigint(_a) / bigint(_b));
|
||||
}
|
||||
|
||||
template <class S> S modWorkaround(S const& _a, S const& _b)
|
||||
{
|
||||
return (S)(bigint(_a) % bigint(_b));
|
||||
}
|
||||
|
||||
/// @returns a list of simplification rules given certain match placeholders.
|
||||
/// A, B and C should represent constants, X and Y arbitrary expressions.
|
||||
/// The simplifications should neven change the order of evaluation of
|
||||
/// arbitrary operations.
|
||||
template <class Pattern>
|
||||
std::vector<SimplificationRule<Pattern>> simplificationRuleList(
|
||||
Pattern A,
|
||||
Pattern B,
|
||||
Pattern C,
|
||||
Pattern X,
|
||||
Pattern Y
|
||||
)
|
||||
{
|
||||
std::vector<SimplificationRule<Pattern>> rules;
|
||||
rules += std::vector<SimplificationRule<Pattern>>{
|
||||
// arithmetics on constants
|
||||
{{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }, false},
|
||||
{{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }, false},
|
||||
{{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }, false},
|
||||
{{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }, false},
|
||||
{{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||
{{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }, false},
|
||||
{{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }, false},
|
||||
{{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }, false},
|
||||
{{Instruction::NOT, {A}}, [=]{ return ~A.d(); }, false},
|
||||
{{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }, false},
|
||||
{{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }, false},
|
||||
{{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }, false},
|
||||
{{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }, false},
|
||||
{{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }, false},
|
||||
{{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }, false},
|
||||
{{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }, false},
|
||||
{{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }, false},
|
||||
{{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }, false},
|
||||
{{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }, false},
|
||||
{{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }, false},
|
||||
{{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }, false},
|
||||
{{Instruction::MULMOD, {A, B, C}}, [=]{ return A.d() * B.d(); }, false},
|
||||
{{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 {
|
||||
if (A.d() >= 31)
|
||||
return B.d();
|
||||
unsigned testBit = unsigned(A.d()) * 8 + 7;
|
||||
u256 mask = (u256(1) << testBit) - 1;
|
||||
return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask);
|
||||
}, false},
|
||||
|
||||
// invariants involving known constants
|
||||
{{Instruction::ADD, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::ADD, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::SUB, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MUL, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MUL, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Instruction::MUL, {1, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::MUL, {X, u256(-1)}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
|
||||
{{Instruction::MUL, {u256(-1), X}}, [=]() -> Pattern { return {Instruction::SUB, {0, X}}; }, false},
|
||||
{{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::DIV, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::DIV, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SDIV, {X, 1}}, [=]{ return X; }, false},
|
||||
{{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }, false},
|
||||
{{Instruction::AND, {~u256(0), X}}, [=]{ return X; }, false},
|
||||
{{Instruction::AND, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::AND, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::OR, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::OR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::OR, {~u256(0), X}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::XOR, {X, 0}}, [=]{ return X; }, false},
|
||||
{{Instruction::XOR, {0, X}}, [=]{ return X; }, false},
|
||||
{{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MOD, {0, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
|
||||
{{Instruction::EQ, {0, X}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; }, false },
|
||||
|
||||
// operations involving an expression and itself
|
||||
{{Instruction::AND, {X, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {X, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::XOR, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SUB, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::EQ, {X, X}}, [=]{ return u256(1); }, true},
|
||||
{{Instruction::LT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SLT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::GT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::SGT, {X, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::MOD, {X, X}}, [=]{ return u256(0); }, true},
|
||||
|
||||
// logical instruction combinations
|
||||
{{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }, false},
|
||||
{{Instruction::XOR, {X, {Instruction::XOR, {X, Y}}}}, [=]{ return Y; }, true},
|
||||
{{Instruction::XOR, {X, {Instruction::XOR, {Y, X}}}}, [=]{ return Y; }, true},
|
||||
{{Instruction::XOR, {{Instruction::XOR, {X, Y}}, X}}, [=]{ return Y; }, true},
|
||||
{{Instruction::XOR, {{Instruction::XOR, {Y, X}}, X}}, [=]{ return Y; }, true},
|
||||
{{Instruction::OR, {X, {Instruction::AND, {X, Y}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {X, {Instruction::AND, {Y, X}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {{Instruction::AND, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::OR, {{Instruction::AND, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {X, {Instruction::OR, {X, Y}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {X, {Instruction::OR, {Y, X}}}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {{Instruction::OR, {X, Y}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {{Instruction::OR, {Y, X}}, X}}, [=]{ return X; }, true},
|
||||
{{Instruction::AND, {X, {Instruction::NOT, {X}}}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::AND, {{Instruction::NOT, {X}}, X}}, [=]{ return u256(0); }, true},
|
||||
{{Instruction::OR, {X, {Instruction::NOT, {X}}}}, [=]{ return ~u256(0); }, true},
|
||||
{{Instruction::OR, {{Instruction::NOT, {X}}, X}}, [=]{ return ~u256(0); }, true},
|
||||
};
|
||||
|
||||
// Replace MOD X, <power-of-two> with AND X, <power-of-two> - 1
|
||||
for (size_t i = 0; i < 256; ++i)
|
||||
{
|
||||
u256 value = u256(1) << i;
|
||||
rules.push_back({
|
||||
{Instruction::MOD, {X, value}},
|
||||
[=]() -> Pattern { return {Instruction::AND, {X, value - 1}}; },
|
||||
false
|
||||
});
|
||||
}
|
||||
|
||||
// Double negation of opcodes with boolean result
|
||||
for (auto const& op: std::vector<Instruction>{
|
||||
Instruction::EQ,
|
||||
Instruction::LT,
|
||||
Instruction::SLT,
|
||||
Instruction::GT,
|
||||
Instruction::SGT
|
||||
})
|
||||
rules.push_back({
|
||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}},
|
||||
[=]() -> Pattern { return {op, {X, Y}}; },
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}},
|
||||
[=]() -> Pattern { return {Instruction::ISZERO, {X}}; },
|
||||
false
|
||||
});
|
||||
|
||||
rules.push_back({
|
||||
{Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}},
|
||||
[=]() -> Pattern { return { Instruction::EQ, {X, Y} }; },
|
||||
false
|
||||
});
|
||||
|
||||
// Associative operations
|
||||
for (auto const& opFun: std::vector<std::pair<Instruction,std::function<u256(u256 const&,u256 const&)>>>{
|
||||
{Instruction::ADD, std::plus<u256>()},
|
||||
{Instruction::MUL, std::multiplies<u256>()},
|
||||
{Instruction::AND, std::bit_and<u256>()},
|
||||
{Instruction::OR, std::bit_or<u256>()},
|
||||
{Instruction::XOR, std::bit_xor<u256>()}
|
||||
})
|
||||
{
|
||||
auto op = opFun.first;
|
||||
auto fun = opFun.second;
|
||||
// Moving constants to the outside, order matters here - we first add rules
|
||||
// for constants and then for non-constants.
|
||||
// xa can be (X, A) or (A, X)
|
||||
for (auto xa: {std::vector<Pattern>{X, A}, std::vector<Pattern>{A, X}})
|
||||
{
|
||||
rules += std::vector<SimplificationRule<Pattern>>{{
|
||||
// (X+A)+B -> X+(A+B)
|
||||
{op, {{op, xa}, B}},
|
||||
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; },
|
||||
false
|
||||
}, {
|
||||
// (X+A)+Y -> (X+Y)+A
|
||||
{op, {{op, xa}, Y}},
|
||||
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; },
|
||||
false
|
||||
}, {
|
||||
// B+(X+A) -> X+(A+B)
|
||||
{op, {B, {op, xa}}},
|
||||
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; },
|
||||
false
|
||||
}, {
|
||||
// Y+(X+A) -> (Y+X)+A
|
||||
{op, {Y, {op, xa}}},
|
||||
[=]() -> Pattern { return {op, {{op, {Y, X}}, A}}; },
|
||||
false
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
// move constants across subtractions
|
||||
rules += std::vector<SimplificationRule<Pattern>>{
|
||||
{
|
||||
// X - A -> X + (-A)
|
||||
{Instruction::SUB, {X, A}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; },
|
||||
false
|
||||
}, {
|
||||
// (X + A) - Y -> (X - Y) + A
|
||||
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
|
||||
false
|
||||
}, {
|
||||
// (A + X) - Y -> (X - Y) + A
|
||||
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; },
|
||||
false
|
||||
}, {
|
||||
// X - (Y + A) -> (X - Y) + (-A)
|
||||
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
false
|
||||
}, {
|
||||
// X - (A + Y) -> (X - Y) + (-A)
|
||||
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; },
|
||||
false
|
||||
}
|
||||
};
|
||||
return rules;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -35,6 +35,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
|
||||
default:
|
||||
case UndefinedItem:
|
||||
case Tag:
|
||||
case PushDeployTimeAddress:
|
||||
return true;
|
||||
case Push:
|
||||
case PushString:
|
||||
@ -153,6 +154,31 @@ bool SemanticInformation::isDeterministic(AssemblyItem const& _item)
|
||||
}
|
||||
}
|
||||
|
||||
bool SemanticInformation::movable(Instruction _instruction)
|
||||
{
|
||||
// These are not really functional.
|
||||
if (isDupInstruction(_instruction) || isSwapInstruction(_instruction))
|
||||
return false;
|
||||
InstructionInfo info = instructionInfo(_instruction);
|
||||
if (info.sideEffects)
|
||||
return false;
|
||||
switch (_instruction)
|
||||
{
|
||||
case Instruction::KECCAK256:
|
||||
case Instruction::BALANCE:
|
||||
case Instruction::EXTCODESIZE:
|
||||
case Instruction::RETURNDATASIZE:
|
||||
case Instruction::SLOAD:
|
||||
case Instruction::PC:
|
||||
case Instruction::MSIZE:
|
||||
case Instruction::GAS:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemanticInformation::invalidatesMemory(Instruction _instruction)
|
||||
{
|
||||
switch (_instruction)
|
||||
|
@ -49,6 +49,10 @@ struct SemanticInformation
|
||||
/// @returns false if the value put on the stack by _item depends on anything else than
|
||||
/// the information in the current block header, memory, storage or stack.
|
||||
static bool isDeterministic(AssemblyItem const& _item);
|
||||
/// @returns true if the instruction can be moved or copied (together with its arguments)
|
||||
/// without altering the semantics. This means it cannot depend on storage or memory,
|
||||
/// cannot have any side-effects, but it can depend on a call-constant state of the blockchain.
|
||||
static bool movable(solidity::Instruction _instruction);
|
||||
/// @returns true if the given instruction modifies memory.
|
||||
static bool invalidatesMemory(solidity::Instruction _instruction);
|
||||
/// @returns true if the given instruction modifies storage (even indirectly).
|
||||
|
45
libevmasm/SimplificationRule.h
Normal file
45
libevmasm/SimplificationRule.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Expression simplification pattern.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
/**
|
||||
* Rule that contains a pattern, an action that can be applied
|
||||
* after the pattern has matched and a bool that indicates
|
||||
* whether the action would remove something from the expression
|
||||
* than is not a constant literal.
|
||||
*/
|
||||
template <class Pattern>
|
||||
struct SimplificationRule
|
||||
{
|
||||
Pattern pattern;
|
||||
std::function<Pattern()> action;
|
||||
bool removesNonConstants;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
@ -31,12 +30,14 @@
|
||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||
#include <libevmasm/SimplificationRules.h>
|
||||
|
||||
#include <libevmasm/RuleList.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::eth;
|
||||
|
||||
|
||||
pair<Pattern, function<Pattern()> > const* Rules::findFirstMatch(
|
||||
SimplificationRule<Pattern> const* Rules::findFirstMatch(
|
||||
Expression const& _expr,
|
||||
ExpressionClasses const& _classes
|
||||
)
|
||||
@ -46,32 +47,22 @@ pair<Pattern, function<Pattern()> > const* Rules::findFirstMatch(
|
||||
assertThrow(_expr.item, OptimizerException, "");
|
||||
for (auto const& rule: m_rules[byte(_expr.item->instruction())])
|
||||
{
|
||||
if (rule.first.matches(_expr, _classes))
|
||||
if (rule.pattern.matches(_expr, _classes))
|
||||
return &rule;
|
||||
resetMatchGroups();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Rules::addRules(std::vector<std::pair<Pattern, std::function<Pattern ()> > > const& _rules)
|
||||
void Rules::addRules(std::vector<SimplificationRule<Pattern>> const& _rules)
|
||||
{
|
||||
for (auto const& r: _rules)
|
||||
addRule(r);
|
||||
}
|
||||
|
||||
void Rules::addRule(std::pair<Pattern, std::function<Pattern()> > const& _rule)
|
||||
void Rules::addRule(SimplificationRule<Pattern> const& _rule)
|
||||
{
|
||||
m_rules[byte(_rule.first.instruction())].push_back(_rule);
|
||||
}
|
||||
|
||||
template <class S> S divWorkaround(S const& _a, S const& _b)
|
||||
{
|
||||
return (S)(bigint(_a) / bigint(_b));
|
||||
}
|
||||
|
||||
template <class S> S modWorkaround(S const& _a, S const& _b)
|
||||
{
|
||||
return (S)(bigint(_a) % bigint(_b));
|
||||
m_rules[byte(_rule.pattern.instruction())].push_back(_rule);
|
||||
}
|
||||
|
||||
Rules::Rules()
|
||||
@ -84,165 +75,13 @@ Rules::Rules()
|
||||
// Anything.
|
||||
Pattern X;
|
||||
Pattern Y;
|
||||
Pattern Z;
|
||||
A.setMatchGroup(1, m_matchGroups);
|
||||
B.setMatchGroup(2, m_matchGroups);
|
||||
C.setMatchGroup(3, m_matchGroups);
|
||||
X.setMatchGroup(4, m_matchGroups);
|
||||
Y.setMatchGroup(5, m_matchGroups);
|
||||
Z.setMatchGroup(6, m_matchGroups);
|
||||
|
||||
addRules(vector<pair<Pattern, function<Pattern()>>>{
|
||||
// arithmetics on constants
|
||||
{{Instruction::ADD, {A, B}}, [=]{ return A.d() + B.d(); }},
|
||||
{{Instruction::MUL, {A, B}}, [=]{ return A.d() * B.d(); }},
|
||||
{{Instruction::SUB, {A, B}}, [=]{ return A.d() - B.d(); }},
|
||||
{{Instruction::DIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : divWorkaround(A.d(), B.d()); }},
|
||||
{{Instruction::SDIV, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(divWorkaround(u2s(A.d()), u2s(B.d()))); }},
|
||||
{{Instruction::MOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : modWorkaround(A.d(), B.d()); }},
|
||||
{{Instruction::SMOD, {A, B}}, [=]{ return B.d() == 0 ? 0 : s2u(modWorkaround(u2s(A.d()), u2s(B.d()))); }},
|
||||
{{Instruction::EXP, {A, B}}, [=]{ return u256(boost::multiprecision::powm(bigint(A.d()), bigint(B.d()), bigint(1) << 256)); }},
|
||||
{{Instruction::NOT, {A}}, [=]{ return ~A.d(); }},
|
||||
{{Instruction::LT, {A, B}}, [=]() -> u256 { return A.d() < B.d() ? 1 : 0; }},
|
||||
{{Instruction::GT, {A, B}}, [=]() -> u256 { return A.d() > B.d() ? 1 : 0; }},
|
||||
{{Instruction::SLT, {A, B}}, [=]() -> u256 { return u2s(A.d()) < u2s(B.d()) ? 1 : 0; }},
|
||||
{{Instruction::SGT, {A, B}}, [=]() -> u256 { return u2s(A.d()) > u2s(B.d()) ? 1 : 0; }},
|
||||
{{Instruction::EQ, {A, B}}, [=]() -> u256 { return A.d() == B.d() ? 1 : 0; }},
|
||||
{{Instruction::ISZERO, {A}}, [=]() -> u256 { return A.d() == 0 ? 1 : 0; }},
|
||||
{{Instruction::AND, {A, B}}, [=]{ return A.d() & B.d(); }},
|
||||
{{Instruction::OR, {A, B}}, [=]{ return A.d() | B.d(); }},
|
||||
{{Instruction::XOR, {A, B}}, [=]{ return A.d() ^ B.d(); }},
|
||||
{{Instruction::BYTE, {A, B}}, [=]{ return A.d() >= 32 ? 0 : (B.d() >> unsigned(8 * (31 - A.d()))) & 0xff; }},
|
||||
{{Instruction::ADDMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) + bigint(B.d())) % C.d()); }},
|
||||
{{Instruction::MULMOD, {A, B, C}}, [=]{ return C.d() == 0 ? 0 : u256((bigint(A.d()) * bigint(B.d())) % C.d()); }},
|
||||
{{Instruction::MULMOD, {A, B, C}}, [=]{ return A.d() * B.d(); }},
|
||||
{{Instruction::SIGNEXTEND, {A, B}}, [=]() -> u256 {
|
||||
if (A.d() >= 31)
|
||||
return B.d();
|
||||
unsigned testBit = unsigned(A.d()) * 8 + 7;
|
||||
u256 mask = (u256(1) << testBit) - 1;
|
||||
return u256(boost::multiprecision::bit_test(B.d(), testBit) ? B.d() | ~mask : B.d() & mask);
|
||||
}},
|
||||
|
||||
// invariants involving known constants (commutative instructions will be checked with swapped operants too)
|
||||
{{Instruction::ADD, {X, 0}}, [=]{ return X; }},
|
||||
{{Instruction::SUB, {X, 0}}, [=]{ return X; }},
|
||||
{{Instruction::MUL, {X, 0}}, [=]{ return u256(0); }},
|
||||
{{Instruction::MUL, {X, 1}}, [=]{ return X; }},
|
||||
{{Instruction::DIV, {X, 0}}, [=]{ return u256(0); }},
|
||||
{{Instruction::DIV, {0, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::DIV, {X, 1}}, [=]{ return X; }},
|
||||
{{Instruction::SDIV, {X, 0}}, [=]{ return u256(0); }},
|
||||
{{Instruction::SDIV, {0, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::SDIV, {X, 1}}, [=]{ return X; }},
|
||||
{{Instruction::AND, {X, ~u256(0)}}, [=]{ return X; }},
|
||||
{{Instruction::AND, {X, 0}}, [=]{ return u256(0); }},
|
||||
{{Instruction::OR, {X, 0}}, [=]{ return X; }},
|
||||
{{Instruction::OR, {X, ~u256(0)}}, [=]{ return ~u256(0); }},
|
||||
{{Instruction::XOR, {X, 0}}, [=]{ return X; }},
|
||||
{{Instruction::MOD, {X, 0}}, [=]{ return u256(0); }},
|
||||
{{Instruction::MOD, {0, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::EQ, {X, 0}}, [=]() -> Pattern { return {Instruction::ISZERO, {X}}; } },
|
||||
|
||||
// operations involving an expression and itself
|
||||
{{Instruction::AND, {X, X}}, [=]{ return X; }},
|
||||
{{Instruction::OR, {X, X}}, [=]{ return X; }},
|
||||
{{Instruction::XOR, {X, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::SUB, {X, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::EQ, {X, X}}, [=]{ return u256(1); }},
|
||||
{{Instruction::LT, {X, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::SLT, {X, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::GT, {X, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::SGT, {X, X}}, [=]{ return u256(0); }},
|
||||
{{Instruction::MOD, {X, X}}, [=]{ return u256(0); }},
|
||||
|
||||
// logical instruction combinations
|
||||
{{Instruction::NOT, {{Instruction::NOT, {X}}}}, [=]{ return X; }},
|
||||
{{Instruction::XOR, {{{X}, {Instruction::XOR, {X, Y}}}}}, [=]{ return Y; }},
|
||||
{{Instruction::OR, {{{X}, {Instruction::AND, {X, Y}}}}}, [=]{ return X; }},
|
||||
{{Instruction::AND, {{{X}, {Instruction::OR, {X, Y}}}}}, [=]{ return X; }},
|
||||
{{Instruction::AND, {{{X}, {Instruction::NOT, {X}}}}}, [=]{ return u256(0); }},
|
||||
{{Instruction::OR, {{{X}, {Instruction::NOT, {X}}}}}, [=]{ return ~u256(0); }},
|
||||
});
|
||||
|
||||
// Double negation of opcodes with binary result
|
||||
for (auto const& op: vector<Instruction>{
|
||||
Instruction::EQ,
|
||||
Instruction::LT,
|
||||
Instruction::SLT,
|
||||
Instruction::GT,
|
||||
Instruction::SGT
|
||||
})
|
||||
addRule({
|
||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{op, {X, Y}}}}}},
|
||||
[=]() -> Pattern { return {op, {X, Y}}; }
|
||||
});
|
||||
|
||||
addRule({
|
||||
{Instruction::ISZERO, {{Instruction::ISZERO, {{Instruction::ISZERO, {X}}}}}},
|
||||
[=]() -> Pattern { return {Instruction::ISZERO, {X}}; }
|
||||
});
|
||||
|
||||
addRule({
|
||||
{Instruction::ISZERO, {{Instruction::XOR, {X, Y}}}},
|
||||
[=]() -> Pattern { return { Instruction::EQ, {X, Y} }; }
|
||||
});
|
||||
|
||||
// Associative operations
|
||||
for (auto const& opFun: vector<pair<Instruction,function<u256(u256 const&,u256 const&)>>>{
|
||||
{Instruction::ADD, plus<u256>()},
|
||||
{Instruction::MUL, multiplies<u256>()},
|
||||
{Instruction::AND, bit_and<u256>()},
|
||||
{Instruction::OR, bit_or<u256>()},
|
||||
{Instruction::XOR, bit_xor<u256>()}
|
||||
})
|
||||
{
|
||||
auto op = opFun.first;
|
||||
auto fun = opFun.second;
|
||||
// Moving constants to the outside, order matters here!
|
||||
// we need actions that return expressions (or patterns?) here, and we need also reversed rules
|
||||
// (X+A)+B -> X+(A+B)
|
||||
addRules(vector<pair<Pattern, function<Pattern()>>>{{
|
||||
{op, {{op, {X, A}}, B}},
|
||||
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
|
||||
}, {
|
||||
// X+(Y+A) -> (X+Y)+A
|
||||
{op, {{op, {X, A}}, Y}},
|
||||
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
|
||||
}, {
|
||||
// For now, we still need explicit commutativity for the inner pattern
|
||||
{op, {{op, {A, X}}, B}},
|
||||
[=]() -> Pattern { return {op, {X, fun(A.d(), B.d())}}; }
|
||||
}, {
|
||||
{op, {{op, {A, X}}, Y}},
|
||||
[=]() -> Pattern { return {op, {{op, {X, Y}}, A}}; }
|
||||
}});
|
||||
}
|
||||
|
||||
// move constants across subtractions
|
||||
addRules(vector<pair<Pattern, function<Pattern()>>>{
|
||||
{
|
||||
// X - A -> X + (-A)
|
||||
{Instruction::SUB, {X, A}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {X, 0 - A.d()}}; }
|
||||
}, {
|
||||
// (X + A) - Y -> (X - Y) + A
|
||||
{Instruction::SUB, {{Instruction::ADD, {X, A}}, Y}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
|
||||
}, {
|
||||
// (A + X) - Y -> (X - Y) + A
|
||||
{Instruction::SUB, {{Instruction::ADD, {A, X}}, Y}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, A}}; }
|
||||
}, {
|
||||
// X - (Y + A) -> (X - Y) + (-A)
|
||||
{Instruction::SUB, {X, {Instruction::ADD, {Y, A}}}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
|
||||
}, {
|
||||
// X - (A + Y) -> (X - Y) + (-A)
|
||||
{Instruction::SUB, {X, {Instruction::ADD, {A, Y}}}},
|
||||
[=]() -> Pattern { return {Instruction::ADD, {{Instruction::SUB, {X, Y}}, 0 - A.d()}}; }
|
||||
}
|
||||
});
|
||||
addRules(simplificationRuleList(A, B, C, X, Y));
|
||||
}
|
||||
|
||||
Pattern::Pattern(Instruction _instruction, std::vector<Pattern> const& _arguments):
|
||||
|
@ -24,6 +24,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <libevmasm/SimplificationRule.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
@ -47,19 +48,21 @@ public:
|
||||
|
||||
/// @returns a pointer to the first matching pattern and sets the match
|
||||
/// groups accordingly.
|
||||
std::pair<Pattern, std::function<Pattern()>> const* findFirstMatch(
|
||||
SimplificationRule<Pattern> const* findFirstMatch(
|
||||
Expression const& _expr,
|
||||
ExpressionClasses const& _classes
|
||||
);
|
||||
|
||||
private:
|
||||
void addRules(std::vector<std::pair<Pattern, std::function<Pattern()>>> const& _rules);
|
||||
void addRule(std::pair<Pattern, std::function<Pattern()>> const& _rule);
|
||||
void addRules(std::vector<SimplificationRule<Pattern>> const& _rules);
|
||||
void addRule(SimplificationRule<Pattern> const& _rule);
|
||||
|
||||
void resetMatchGroups() { m_matchGroups.clear(); }
|
||||
|
||||
std::map<unsigned, Expression const*> m_matchGroups;
|
||||
std::vector<std::pair<Pattern, std::function<Pattern()>>> m_rules[256];
|
||||
/// Pattern to match, replacement to be applied and flag indicating whether
|
||||
/// the replacement might remove some elements (except constants).
|
||||
std::vector<SimplificationRule<Pattern>> m_rules[256];
|
||||
};
|
||||
|
||||
/**
|
||||
|
54
libjulia/ASTDataForward.h
Normal file
54
libjulia/ASTDataForward.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* @date 2017
|
||||
* Pull in some identifiers from the solidity::assembly namespace.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
using Instruction = solidity::assembly::Instruction;
|
||||
using Literal = solidity::assembly::Literal;
|
||||
using Label = solidity::assembly::Label;
|
||||
using StackAssignment = solidity::assembly::StackAssignment;
|
||||
using Identifier = solidity::assembly::Identifier;
|
||||
using Assignment = solidity::assembly::Assignment;
|
||||
using VariableDeclaration = solidity::assembly::VariableDeclaration;
|
||||
using FunctionalInstruction = solidity::assembly::FunctionalInstruction;
|
||||
using FunctionDefinition = solidity::assembly::FunctionDefinition;
|
||||
using FunctionCall = solidity::assembly::FunctionCall;
|
||||
using If = solidity::assembly::If;
|
||||
using Case = solidity::assembly::Case;
|
||||
using Switch = solidity::assembly::Switch;
|
||||
using ForLoop = solidity::assembly::ForLoop;
|
||||
using ExpressionStatement = solidity::assembly::ExpressionStatement;
|
||||
using Block = solidity::assembly::Block;
|
||||
|
||||
using TypedName = solidity::assembly::TypedName;
|
||||
|
||||
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
|
||||
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
|
||||
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace julia;
|
||||
using namespace dev::julia;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -31,7 +31,8 @@ using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
using namespace dev::solidity::assembly;
|
||||
|
||||
using Scope = dev::solidity::assembly::Scope;
|
||||
|
||||
void CodeTransform::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
@ -76,6 +77,13 @@ void CodeTransform::operator()(StackAssignment const& _assignment)
|
||||
checkStackHeight(&_assignment);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
m_assembly.setSourceLocation(_statement.location);
|
||||
boost::apply_visitor(*this, _statement.expression);
|
||||
checkStackHeight(&_statement);
|
||||
}
|
||||
|
||||
void CodeTransform::operator()(Label const& _label)
|
||||
{
|
||||
m_assembly.setSourceLocation(_label.location);
|
||||
@ -124,11 +132,11 @@ void CodeTransform::operator()(FunctionCall const& _call)
|
||||
void CodeTransform::operator()(FunctionalInstruction const& _instruction)
|
||||
{
|
||||
if (m_evm15 && (
|
||||
_instruction.instruction.instruction == solidity::Instruction::JUMP ||
|
||||
_instruction.instruction.instruction == solidity::Instruction::JUMPI
|
||||
_instruction.instruction == solidity::Instruction::JUMP ||
|
||||
_instruction.instruction == solidity::Instruction::JUMPI
|
||||
))
|
||||
{
|
||||
bool const isJumpI = _instruction.instruction.instruction == solidity::Instruction::JUMPI;
|
||||
bool const isJumpI = _instruction.instruction == solidity::Instruction::JUMPI;
|
||||
if (isJumpI)
|
||||
{
|
||||
solAssert(_instruction.arguments.size() == 2, "");
|
||||
@ -149,7 +157,8 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction)
|
||||
{
|
||||
for (auto const& arg: _instruction.arguments | boost::adaptors::reversed)
|
||||
visitExpression(arg);
|
||||
(*this)(_instruction.instruction);
|
||||
m_assembly.setSourceLocation(_instruction.location);
|
||||
m_assembly.appendInstruction(_instruction.instruction);
|
||||
}
|
||||
checkStackHeight(&_instruction);
|
||||
}
|
||||
@ -289,7 +298,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
solAssert(m_info.scopes.at(&_function.body), "");
|
||||
Scope* varScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
|
||||
solAssert(varScope, "");
|
||||
for (auto const& v: _function.arguments | boost::adaptors::reversed)
|
||||
for (auto const& v: _function.parameters | boost::adaptors::reversed)
|
||||
{
|
||||
auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name));
|
||||
m_context->variableStackHeights[&var] = height++;
|
||||
@ -302,7 +311,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
if (m_evm15)
|
||||
{
|
||||
m_assembly.appendJumpTo(afterFunction, -stackHeightBefore);
|
||||
m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.arguments.size());
|
||||
m_assembly.appendBeginsub(functionEntryID(_function.name, function), _function.parameters.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -311,7 +320,7 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
}
|
||||
m_stackAdjustment += localStackAdjustment;
|
||||
|
||||
for (auto const& v: _function.returns)
|
||||
for (auto const& v: _function.returnVariables)
|
||||
{
|
||||
auto& var = boost::get<Scope::Variable>(varScope->identifiers.at(v.name));
|
||||
m_context->variableStackHeights[&var] = height++;
|
||||
@ -341,9 +350,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
// modified parallel to the actual stack.
|
||||
vector<int> stackLayout;
|
||||
if (!m_evm15)
|
||||
stackLayout.push_back(_function.returns.size()); // Move return label to the top
|
||||
stackLayout += vector<int>(_function.arguments.size(), -1); // discard all arguments
|
||||
for (size_t i = 0; i < _function.returns.size(); ++i)
|
||||
stackLayout.push_back(_function.returnVariables.size()); // Move return label to the top
|
||||
stackLayout += vector<int>(_function.parameters.size(), -1); // discard all arguments
|
||||
for (size_t i = 0; i < _function.returnVariables.size(); ++i)
|
||||
stackLayout.push_back(i); // Move return values down, but keep order.
|
||||
|
||||
solAssert(stackLayout.size() <= 17, "Stack too deep");
|
||||
@ -363,9 +372,9 @@ void CodeTransform::operator()(FunctionDefinition const& _function)
|
||||
}
|
||||
|
||||
if (m_evm15)
|
||||
m_assembly.appendReturnsub(_function.returns.size(), stackHeightBefore);
|
||||
m_assembly.appendReturnsub(_function.returnVariables.size(), stackHeightBefore);
|
||||
else
|
||||
m_assembly.appendJump(stackHeightBefore - _function.returns.size());
|
||||
m_assembly.appendJump(stackHeightBefore - _function.returnVariables.size());
|
||||
m_stackAdjustment -= localStackAdjustment;
|
||||
m_assembly.appendLabel(afterFunction);
|
||||
checkStackHeight(&_function);
|
||||
@ -458,7 +467,7 @@ AbstractAssembly::LabelID CodeTransform::functionEntryID(string const& _name, Sc
|
||||
return m_context->functionEntryIDs[&_function];
|
||||
}
|
||||
|
||||
void CodeTransform::visitExpression(Statement const& _expression)
|
||||
void CodeTransform::visitExpression(Expression const& _expression)
|
||||
{
|
||||
int height = m_assembly.stackHeight();
|
||||
boost::apply_visitor(*this, _expression);
|
||||
|
@ -20,8 +20,9 @@
|
||||
|
||||
#include <libjulia/backends/evm/EVMAssembly.h>
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
#include <libsolidity/inlineasm/AsmDataForward.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
@ -95,38 +96,39 @@ protected:
|
||||
{}
|
||||
|
||||
public:
|
||||
void operator()(solidity::assembly::Instruction const& _instruction);
|
||||
void operator()(solidity::assembly::Literal const& _literal);
|
||||
void operator()(solidity::assembly::Identifier const& _identifier);
|
||||
void operator()(solidity::assembly::FunctionalInstruction const& _instr);
|
||||
void operator()(solidity::assembly::FunctionCall const&);
|
||||
void operator()(solidity::assembly::Label const& _label);
|
||||
void operator()(solidity::assembly::StackAssignment const& _assignment);
|
||||
void operator()(solidity::assembly::Assignment const& _assignment);
|
||||
void operator()(solidity::assembly::VariableDeclaration const& _varDecl);
|
||||
void operator()(solidity::assembly::If const& _if);
|
||||
void operator()(solidity::assembly::Switch const& _switch);
|
||||
void operator()(solidity::assembly::FunctionDefinition const&);
|
||||
void operator()(solidity::assembly::ForLoop const&);
|
||||
void operator()(solidity::assembly::Block const& _block);
|
||||
void operator()(Instruction const& _instruction);
|
||||
void operator()(Literal const& _literal);
|
||||
void operator()(Identifier const& _identifier);
|
||||
void operator()(FunctionalInstruction const& _instr);
|
||||
void operator()(FunctionCall const&);
|
||||
void operator()(ExpressionStatement const& _statement);
|
||||
void operator()(Label const& _label);
|
||||
void operator()(StackAssignment const& _assignment);
|
||||
void operator()(Assignment const& _assignment);
|
||||
void operator()(VariableDeclaration const& _varDecl);
|
||||
void operator()(If const& _if);
|
||||
void operator()(Switch const& _switch);
|
||||
void operator()(FunctionDefinition const&);
|
||||
void operator()(ForLoop const&);
|
||||
void operator()(Block const& _block);
|
||||
|
||||
private:
|
||||
AbstractAssembly::LabelID labelFromIdentifier(solidity::assembly::Identifier const& _identifier);
|
||||
AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier);
|
||||
/// @returns the label ID corresponding to the given label, allocating a new one if
|
||||
/// necessary.
|
||||
AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label);
|
||||
AbstractAssembly::LabelID functionEntryID(std::string const& _name, solidity::assembly::Scope::Function const& _function);
|
||||
/// Generates code for an expression that is supposed to return a single value.
|
||||
void visitExpression(solidity::assembly::Statement const& _expression);
|
||||
void visitExpression(Expression const& _expression);
|
||||
|
||||
void visitStatements(std::vector<solidity::assembly::Statement> const& _statements);
|
||||
void visitStatements(std::vector<Statement> const& _statements);
|
||||
|
||||
/// Pops all variables declared in the block and checks that the stack height is equal
|
||||
/// to @a _blackStartStackHeight.
|
||||
void finalizeBlock(solidity::assembly::Block const& _block, int _blockStartStackHeight);
|
||||
void finalizeBlock(Block const& _block, int _blockStartStackHeight);
|
||||
|
||||
void generateMultiAssignment(std::vector<solidity::assembly::Identifier> const& _variableNames);
|
||||
void generateAssignment(solidity::assembly::Identifier const& _variableName);
|
||||
void generateMultiAssignment(std::vector<Identifier> const& _variableNames);
|
||||
void generateAssignment(Identifier const& _variableName);
|
||||
|
||||
/// Determines the stack height difference to the given variables. Throws
|
||||
/// if it is not yet in scope or the height difference is too large. Returns
|
||||
|
185
libjulia/optimiser/ASTCopier.cpp
Normal file
185
libjulia/optimiser/ASTCopier.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Creates an independent copy of an AST, renaming identifiers to be unique.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/ASTCopier.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <libdevcore/Common.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
|
||||
Statement ASTCopier::operator()(Instruction const&)
|
||||
{
|
||||
solAssert(false, "Invalid operation.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
return ExpressionStatement{ _statement.location, translate(_statement.expression) };
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
return VariableDeclaration{
|
||||
_varDecl.location,
|
||||
translateVector(_varDecl.variables),
|
||||
translate(_varDecl.value)
|
||||
};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(Assignment const& _assignment)
|
||||
{
|
||||
return Assignment{
|
||||
_assignment.location,
|
||||
translateVector(_assignment.variableNames),
|
||||
translate(_assignment.value)
|
||||
};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(StackAssignment const&)
|
||||
{
|
||||
solAssert(false, "Invalid operation.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(Label const&)
|
||||
{
|
||||
solAssert(false, "Invalid operation.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Expression ASTCopier::operator()(FunctionCall const& _call)
|
||||
{
|
||||
return FunctionCall{
|
||||
_call.location,
|
||||
translate(_call.functionName),
|
||||
translateVector(_call.arguments)
|
||||
};
|
||||
}
|
||||
|
||||
Expression ASTCopier::operator()(FunctionalInstruction const& _instruction)
|
||||
{
|
||||
return FunctionalInstruction{
|
||||
_instruction.location,
|
||||
_instruction.instruction,
|
||||
translateVector(_instruction.arguments)
|
||||
};
|
||||
}
|
||||
|
||||
Expression ASTCopier::operator()(Identifier const& _identifier)
|
||||
{
|
||||
return Identifier{_identifier.location, translateIdentifier(_identifier.name)};
|
||||
}
|
||||
|
||||
Expression ASTCopier::operator()(Literal const& _literal)
|
||||
{
|
||||
return translate(_literal);
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(If const& _if)
|
||||
{
|
||||
return If{_if.location, translate(_if.condition), translate(_if.body)};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(Switch const& _switch)
|
||||
{
|
||||
return Switch{_switch.location, translate(_switch.expression), translateVector(_switch.cases)};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(FunctionDefinition const& _function)
|
||||
{
|
||||
string translatedName = translateIdentifier(_function.name);
|
||||
|
||||
enterFunction(_function);
|
||||
ScopeGuard g([&]() { this->leaveFunction(_function); });
|
||||
|
||||
return FunctionDefinition{
|
||||
_function.location,
|
||||
move(translatedName),
|
||||
translateVector(_function.parameters),
|
||||
translateVector(_function.returnVariables),
|
||||
translate(_function.body)
|
||||
};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator()(ForLoop const& _forLoop)
|
||||
{
|
||||
enterScope(_forLoop.pre);
|
||||
ScopeGuard g([&]() { this->leaveScope(_forLoop.pre); });
|
||||
|
||||
return ForLoop{
|
||||
_forLoop.location,
|
||||
translate(_forLoop.pre),
|
||||
translate(_forLoop.condition),
|
||||
translate(_forLoop.post),
|
||||
translate(_forLoop.body)
|
||||
};
|
||||
}
|
||||
|
||||
Statement ASTCopier::operator ()(Block const& _block)
|
||||
{
|
||||
return translate(_block);
|
||||
}
|
||||
|
||||
Expression ASTCopier::translate(Expression const& _expression)
|
||||
{
|
||||
return _expression.apply_visitor(static_cast<ExpressionCopier&>(*this));
|
||||
}
|
||||
|
||||
Statement ASTCopier::translate(Statement const& _statement)
|
||||
{
|
||||
return _statement.apply_visitor(static_cast<StatementCopier&>(*this));
|
||||
}
|
||||
|
||||
Block ASTCopier::translate(Block const& _block)
|
||||
{
|
||||
enterScope(_block);
|
||||
ScopeGuard g([&]() { this->leaveScope(_block); });
|
||||
|
||||
return Block{_block.location, translateVector(_block.statements)};
|
||||
}
|
||||
|
||||
Case ASTCopier::translate(Case const& _case)
|
||||
{
|
||||
return Case{_case.location, translate(_case.value), translate(_case.body)};
|
||||
}
|
||||
|
||||
Identifier ASTCopier::translate(Identifier const& _identifier)
|
||||
{
|
||||
return Identifier{_identifier.location, translateIdentifier(_identifier.name)};
|
||||
}
|
||||
|
||||
Literal ASTCopier::translate(Literal const& _literal)
|
||||
{
|
||||
return _literal;
|
||||
}
|
||||
|
||||
TypedName ASTCopier::translate(TypedName const& _typedName)
|
||||
{
|
||||
return TypedName{_typedName.location, translateIdentifier(_typedName.name), _typedName.type};
|
||||
}
|
||||
|
121
libjulia/optimiser/ASTCopier.h
Normal file
121
libjulia/optimiser/ASTCopier.h
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Creates an independent copy of an AST, renaming identifiers to be unique.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
class ExpressionCopier: public boost::static_visitor<Expression>
|
||||
{
|
||||
public:
|
||||
virtual Expression operator()(Literal const& _literal) = 0;
|
||||
virtual Expression operator()(Identifier const& _identifier) = 0;
|
||||
virtual Expression operator()(FunctionalInstruction const& _instr) = 0;
|
||||
virtual Expression operator()(FunctionCall const&) = 0;
|
||||
};
|
||||
|
||||
class StatementCopier: public boost::static_visitor<Statement>
|
||||
{
|
||||
public:
|
||||
virtual Statement operator()(ExpressionStatement const& _statement) = 0;
|
||||
virtual Statement operator()(Instruction const& _instruction) = 0;
|
||||
virtual Statement operator()(Label const& _label) = 0;
|
||||
virtual Statement operator()(StackAssignment const& _assignment) = 0;
|
||||
virtual Statement operator()(Assignment const& _assignment) = 0;
|
||||
virtual Statement operator()(VariableDeclaration const& _varDecl) = 0;
|
||||
virtual Statement operator()(If const& _if) = 0;
|
||||
virtual Statement operator()(Switch const& _switch) = 0;
|
||||
virtual Statement operator()(FunctionDefinition const&) = 0;
|
||||
virtual Statement operator()(ForLoop const&) = 0;
|
||||
virtual Statement operator()(Block const& _block) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a copy of a iulia AST potentially replacing identifier names.
|
||||
* Base class to be extended.
|
||||
*/
|
||||
class ASTCopier: public ExpressionCopier, public StatementCopier
|
||||
{
|
||||
public:
|
||||
virtual Expression operator()(Literal const& _literal) override;
|
||||
virtual Statement operator()(Instruction const& _instruction) override;
|
||||
virtual Expression operator()(Identifier const& _identifier) override;
|
||||
virtual Expression operator()(FunctionalInstruction const& _instr) override;
|
||||
virtual Expression operator()(FunctionCall const&) override;
|
||||
virtual Statement operator()(ExpressionStatement const& _statement) override;
|
||||
virtual Statement operator()(Label const& _label) override;
|
||||
virtual Statement operator()(StackAssignment const& _assignment) override;
|
||||
virtual Statement operator()(Assignment const& _assignment) override;
|
||||
virtual Statement operator()(VariableDeclaration const& _varDecl) override;
|
||||
virtual Statement operator()(If const& _if) override;
|
||||
virtual Statement operator()(Switch const& _switch) override;
|
||||
virtual Statement operator()(FunctionDefinition const&) override;
|
||||
virtual Statement operator()(ForLoop const&) override;
|
||||
virtual Statement operator()(Block const& _block) override;
|
||||
|
||||
virtual Expression translate(Expression const& _expression);
|
||||
virtual Statement translate(Statement const& _statement);
|
||||
|
||||
protected:
|
||||
template <typename T>
|
||||
std::vector<T> translateVector(std::vector<T> const& _values);
|
||||
|
||||
template <typename T>
|
||||
std::shared_ptr<T> translate(std::shared_ptr<T> const& _v)
|
||||
{
|
||||
return _v ? std::make_shared<T>(translate(*_v)) : nullptr;
|
||||
}
|
||||
Block translate(Block const& _block);
|
||||
Case translate(Case const& _case);
|
||||
Identifier translate(Identifier const& _identifier);
|
||||
Literal translate(Literal const& _literal);
|
||||
TypedName translate(TypedName const& _typedName);
|
||||
|
||||
virtual void enterScope(Block const&) { }
|
||||
virtual void leaveScope(Block const&) { }
|
||||
virtual void enterFunction(FunctionDefinition const&) { }
|
||||
virtual void leaveFunction(FunctionDefinition const&) { }
|
||||
virtual std::string translateIdentifier(std::string const& _name) { return _name; }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values)
|
||||
{
|
||||
std::vector<T> translated;
|
||||
for (auto const& v: _values)
|
||||
translated.emplace_back(translate(v));
|
||||
return translated;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
159
libjulia/optimiser/ASTWalker.cpp
Normal file
159
libjulia/optimiser/ASTWalker.cpp
Normal file
@ -0,0 +1,159 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Generic AST walker.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
|
||||
void ASTWalker::operator()(FunctionalInstruction const& _instr)
|
||||
{
|
||||
walkVector(_instr.arguments | boost::adaptors::reversed);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(FunctionCall const& _funCall)
|
||||
{
|
||||
walkVector(_funCall.arguments | boost::adaptors::reversed);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(ExpressionStatement const& _statement)
|
||||
{
|
||||
visit(_statement.expression);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(Assignment const& _assignment)
|
||||
{
|
||||
for (auto const& name: _assignment.variableNames)
|
||||
(*this)(name);
|
||||
visit(*_assignment.value);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
if (_varDecl.value)
|
||||
visit(*_varDecl.value);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(If const& _if)
|
||||
{
|
||||
visit(*_if.condition);
|
||||
(*this)(_if.body);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(Switch const& _switch)
|
||||
{
|
||||
visit(*_switch.expression);
|
||||
for (auto const& _case: _switch.cases)
|
||||
{
|
||||
if (_case.value)
|
||||
(*this)(*_case.value);
|
||||
(*this)(_case.body);
|
||||
}
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(FunctionDefinition const& _fun)
|
||||
{
|
||||
(*this)(_fun.body);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(ForLoop const& _for)
|
||||
{
|
||||
(*this)(_for.pre);
|
||||
visit(*_for.condition);
|
||||
(*this)(_for.body);
|
||||
(*this)(_for.post);
|
||||
}
|
||||
|
||||
void ASTWalker::operator()(Block const& _block)
|
||||
{
|
||||
walkVector(_block.statements);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(FunctionalInstruction& _instr)
|
||||
{
|
||||
walkVector(_instr.arguments | boost::adaptors::reversed);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(FunctionCall& _funCall)
|
||||
{
|
||||
walkVector(_funCall.arguments | boost::adaptors::reversed);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(ExpressionStatement& _statement)
|
||||
{
|
||||
visit(_statement.expression);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Assignment& _assignment)
|
||||
{
|
||||
for (auto& name: _assignment.variableNames)
|
||||
(*this)(name);
|
||||
visit(*_assignment.value);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(VariableDeclaration& _varDecl)
|
||||
{
|
||||
if (_varDecl.value)
|
||||
visit(*_varDecl.value);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(If& _if)
|
||||
{
|
||||
visit(*_if.condition);
|
||||
(*this)(_if.body);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Switch& _switch)
|
||||
{
|
||||
visit(*_switch.expression);
|
||||
for (auto& _case: _switch.cases)
|
||||
{
|
||||
if (_case.value)
|
||||
(*this)(*_case.value);
|
||||
(*this)(_case.body);
|
||||
}
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(FunctionDefinition& _fun)
|
||||
{
|
||||
(*this)(_fun.body);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(ForLoop& _for)
|
||||
{
|
||||
(*this)(_for.pre);
|
||||
visit(*_for.condition);
|
||||
(*this)(_for.post);
|
||||
(*this)(_for.body);
|
||||
}
|
||||
|
||||
void ASTModifier::operator()(Block& _block)
|
||||
{
|
||||
walkVector(_block.statements);
|
||||
}
|
120
libjulia/optimiser/ASTWalker.h
Normal file
120
libjulia/optimiser/ASTWalker.h
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Generic AST walker.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Generic AST walker.
|
||||
*/
|
||||
class ASTWalker: public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
virtual void operator()(Literal const&) {}
|
||||
virtual void operator()(Instruction const&) { solAssert(false, ""); }
|
||||
virtual void operator()(Identifier const&) {}
|
||||
virtual void operator()(FunctionalInstruction const& _instr);
|
||||
virtual void operator()(FunctionCall const& _funCall);
|
||||
virtual void operator()(ExpressionStatement const& _statement);
|
||||
virtual void operator()(Label const&) { solAssert(false, ""); }
|
||||
virtual void operator()(StackAssignment const&) { solAssert(false, ""); }
|
||||
virtual void operator()(Assignment const& _assignment);
|
||||
virtual void operator()(VariableDeclaration const& _varDecl);
|
||||
virtual void operator()(If const& _if);
|
||||
virtual void operator()(Switch const& _switch);
|
||||
virtual void operator()(FunctionDefinition const&);
|
||||
virtual void operator()(ForLoop const&);
|
||||
virtual void operator()(Block const& _block);
|
||||
|
||||
virtual void visit(Statement const& _st)
|
||||
{
|
||||
boost::apply_visitor(*this, _st);
|
||||
}
|
||||
virtual void visit(Expression const& _e)
|
||||
{
|
||||
boost::apply_visitor(*this, _e);
|
||||
}
|
||||
|
||||
protected:
|
||||
template <class T>
|
||||
void walkVector(T const& _statements)
|
||||
{
|
||||
for (auto const& statement: _statements)
|
||||
visit(statement);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic AST modifier (i.e. non-const version of ASTWalker).
|
||||
*/
|
||||
class ASTModifier: public boost::static_visitor<>
|
||||
{
|
||||
public:
|
||||
virtual void operator()(Literal&) {}
|
||||
virtual void operator()(Instruction&) { solAssert(false, ""); }
|
||||
virtual void operator()(Identifier&) {}
|
||||
virtual void operator()(FunctionalInstruction& _instr);
|
||||
virtual void operator()(FunctionCall& _funCall);
|
||||
virtual void operator()(ExpressionStatement& _statement);
|
||||
virtual void operator()(Label&) { solAssert(false, ""); }
|
||||
virtual void operator()(StackAssignment&) { solAssert(false, ""); }
|
||||
virtual void operator()(Assignment& _assignment);
|
||||
virtual void operator()(VariableDeclaration& _varDecl);
|
||||
virtual void operator()(If& _if);
|
||||
virtual void operator()(Switch& _switch);
|
||||
virtual void operator()(FunctionDefinition&);
|
||||
virtual void operator()(ForLoop&);
|
||||
virtual void operator()(Block& _block);
|
||||
|
||||
virtual void visit(Statement& _st)
|
||||
{
|
||||
boost::apply_visitor(*this, _st);
|
||||
}
|
||||
virtual void visit(Expression& _e)
|
||||
{
|
||||
boost::apply_visitor(*this, _e);
|
||||
}
|
||||
|
||||
protected:
|
||||
template <class T>
|
||||
void walkVector(T&& _statements)
|
||||
{
|
||||
for (auto& st: _statements)
|
||||
visit(st);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
193
libjulia/optimiser/DataFlowAnalyzer.cpp
Normal file
193
libjulia/optimiser/DataFlowAnalyzer.cpp
Normal file
@ -0,0 +1,193 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Base class to perform data flaw analysis during AST walks.
|
||||
* Tracks assignments and is used as base class for both Rematerialiser and
|
||||
* Common Subexpression Eliminator.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/DataFlowAnalyzer.h>
|
||||
|
||||
#include <libjulia/optimiser/NameCollector.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
void DataFlowAnalyzer::operator()(Assignment& _assignment)
|
||||
{
|
||||
set<string> names;
|
||||
for (auto const& var: _assignment.variableNames)
|
||||
names.insert(var.name);
|
||||
solAssert(_assignment.value, "");
|
||||
visit(*_assignment.value);
|
||||
handleAssignment(names, _assignment.value.get());
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(VariableDeclaration& _varDecl)
|
||||
{
|
||||
set<string> names;
|
||||
for (auto const& var: _varDecl.variables)
|
||||
names.insert(var.name);
|
||||
m_variableScopes.back().variables += names;
|
||||
if (_varDecl.value)
|
||||
visit(*_varDecl.value);
|
||||
handleAssignment(names, _varDecl.value.get());
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(If& _if)
|
||||
{
|
||||
ASTModifier::operator()(_if);
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_if.body);
|
||||
clearValues(assignments.names());
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(Switch& _switch)
|
||||
{
|
||||
visit(*_switch.expression);
|
||||
set<string> assignedVariables;
|
||||
for (auto& _case: _switch.cases)
|
||||
{
|
||||
(*this)(_case.body);
|
||||
Assignments assignments;
|
||||
assignments(_case.body);
|
||||
assignedVariables += assignments.names();
|
||||
// This is a little too destructive, we could retain the old values.
|
||||
clearValues(assignments.names());
|
||||
}
|
||||
clearValues(assignedVariables);
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
|
||||
{
|
||||
m_variableScopes.emplace_back(true);
|
||||
for (auto const& parameter: _fun.parameters)
|
||||
m_variableScopes.back().variables.insert(parameter.name);
|
||||
for (auto const& var: _fun.returnVariables)
|
||||
m_variableScopes.back().variables.insert(var.name);
|
||||
ASTModifier::operator()(_fun);
|
||||
m_variableScopes.pop_back();
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(ForLoop& _for)
|
||||
{
|
||||
// Special scope handling of the pre block.
|
||||
m_variableScopes.emplace_back(false);
|
||||
for (auto& statement: _for.pre.statements)
|
||||
visit(statement);
|
||||
|
||||
Assignments assignments;
|
||||
assignments(_for.body);
|
||||
assignments(_for.post);
|
||||
clearValues(assignments.names());
|
||||
|
||||
visit(*_for.condition);
|
||||
(*this)(_for.body);
|
||||
(*this)(_for.post);
|
||||
|
||||
clearValues(assignments.names());
|
||||
|
||||
m_variableScopes.pop_back();
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::operator()(Block& _block)
|
||||
{
|
||||
size_t numScopes = m_variableScopes.size();
|
||||
m_variableScopes.emplace_back(false);
|
||||
ASTModifier::operator()(_block);
|
||||
m_variableScopes.pop_back();
|
||||
solAssert(numScopes == m_variableScopes.size(), "");
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expression* _value)
|
||||
{
|
||||
clearValues(_variables);
|
||||
|
||||
MovableChecker movableChecker;
|
||||
if (_value)
|
||||
movableChecker.visit(*_value);
|
||||
if (_variables.size() == 1)
|
||||
{
|
||||
string const& name = *_variables.begin();
|
||||
// Expression has to be movable and cannot contain a reference
|
||||
// to the variable that will be assigned to.
|
||||
if (_value && movableChecker.movable() && !movableChecker.referencedVariables().count(name))
|
||||
m_value[name] = _value;
|
||||
}
|
||||
|
||||
auto const& referencedVariables = movableChecker.referencedVariables();
|
||||
for (auto const& name: _variables)
|
||||
{
|
||||
m_references[name] = referencedVariables;
|
||||
for (auto const& ref: referencedVariables)
|
||||
m_referencedBy[ref].insert(name);
|
||||
}
|
||||
}
|
||||
|
||||
void DataFlowAnalyzer::clearValues(set<string> const& _variables)
|
||||
{
|
||||
// All variables that reference variables to be cleared also have to be
|
||||
// cleared, but not recursively, since only the value of the original
|
||||
// variables changes. Example:
|
||||
// let a := 1
|
||||
// let b := a
|
||||
// let c := b
|
||||
// let a := 2
|
||||
// add(b, c)
|
||||
// In the last line, we can replace c by b, but not b by a.
|
||||
//
|
||||
// This cannot be easily tested since the substitutions will be done
|
||||
// one by one on the fly, and the last line will just be add(1, 1)
|
||||
|
||||
set<string> variables = _variables;
|
||||
// Clear variables that reference variables to be cleared.
|
||||
for (auto const& name: variables)
|
||||
for (auto const& ref: m_referencedBy[name])
|
||||
variables.insert(ref);
|
||||
|
||||
// Clear the value and update the reference relation.
|
||||
for (auto const& name: variables)
|
||||
m_value.erase(name);
|
||||
for (auto const& name: variables)
|
||||
{
|
||||
for (auto const& ref: m_references[name])
|
||||
m_referencedBy[ref].erase(name);
|
||||
m_references[name].clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool DataFlowAnalyzer::inScope(string const& _variableName) const
|
||||
{
|
||||
for (auto const& scope: m_variableScopes | boost::adaptors::reversed)
|
||||
{
|
||||
if (scope.variables.count(_variableName))
|
||||
return true;
|
||||
if (scope.isFunction)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
84
libjulia/optimiser/DataFlowAnalyzer.h
Normal file
84
libjulia/optimiser/DataFlowAnalyzer.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Base class to perform data flow analysis during AST walks.
|
||||
* Tracks assignments and is used as base class for both Rematerialiser and
|
||||
* Common Subexpression Eliminator.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Base class to perform data flow analysis during AST walks.
|
||||
* Tracks assignments and is used as base class for both Rematerialiser and
|
||||
* Common Subexpression Eliminator.
|
||||
*
|
||||
* Prerequisite: Disambiguator
|
||||
*/
|
||||
class DataFlowAnalyzer: public ASTModifier
|
||||
{
|
||||
public:
|
||||
using ASTModifier::operator();
|
||||
virtual void operator()(Assignment& _assignment) override;
|
||||
virtual void operator()(VariableDeclaration& _varDecl) override;
|
||||
virtual void operator()(If& _if) override;
|
||||
virtual void operator()(Switch& _switch) override;
|
||||
virtual void operator()(FunctionDefinition&) override;
|
||||
virtual void operator()(ForLoop&) override;
|
||||
virtual void operator()(Block& _block) override;
|
||||
|
||||
protected:
|
||||
/// Registers the assignment.
|
||||
void handleAssignment(std::set<std::string> const& _names, Expression* _value);
|
||||
|
||||
/// Clears information about the valuse assigned to the given variables,
|
||||
/// for example at points where control flow is merged.
|
||||
void clearValues(std::set<std::string> const& _names);
|
||||
|
||||
/// Returns true iff the variable is in scope.
|
||||
bool inScope(std::string const& _variableName) const;
|
||||
|
||||
/// Current values of variables, always movable.
|
||||
std::map<std::string, Expression const*> m_value;
|
||||
/// m_references[a].contains(b) <=> the current expression assigned to a references b
|
||||
std::map<std::string, std::set<std::string>> m_references;
|
||||
/// m_referencedBy[b].contains(a) <=> the current expression assigned to a references b
|
||||
std::map<std::string, std::set<std::string>> m_referencedBy;
|
||||
|
||||
struct Scope
|
||||
{
|
||||
explicit Scope(bool _isFunction): isFunction(_isFunction) {}
|
||||
std::set<std::string> variables;
|
||||
bool isFunction;
|
||||
};
|
||||
/// List of scopes.
|
||||
std::vector<Scope> m_variableScopes;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
85
libjulia/optimiser/Disambiguator.cpp
Normal file
85
libjulia/optimiser/Disambiguator.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that makes all identifiers unique.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/Disambiguator.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/inlineasm/AsmScope.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
using Scope = dev::solidity::assembly::Scope;
|
||||
|
||||
string Disambiguator::translateIdentifier(string const& _originalName)
|
||||
{
|
||||
solAssert(!m_scopes.empty() && m_scopes.back(), "");
|
||||
Scope::Identifier const* id = m_scopes.back()->lookup(_originalName);
|
||||
solAssert(id, "");
|
||||
if (!m_translations.count(id))
|
||||
{
|
||||
string translated = _originalName;
|
||||
size_t suffix = 0;
|
||||
while (m_usedNames.count(translated))
|
||||
{
|
||||
suffix++;
|
||||
translated = _originalName + "_" + std::to_string(suffix);
|
||||
}
|
||||
m_usedNames.insert(translated);
|
||||
m_translations[id] = translated;
|
||||
}
|
||||
return m_translations.at(id);
|
||||
}
|
||||
|
||||
void Disambiguator::enterScope(Block const& _block)
|
||||
{
|
||||
enterScopeInternal(*m_info.scopes.at(&_block));
|
||||
}
|
||||
|
||||
void Disambiguator::leaveScope(Block const& _block)
|
||||
{
|
||||
leaveScopeInternal(*m_info.scopes.at(&_block));
|
||||
}
|
||||
|
||||
void Disambiguator::enterFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
enterScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()));
|
||||
}
|
||||
|
||||
void Disambiguator::leaveFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
leaveScopeInternal(*m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()));
|
||||
}
|
||||
|
||||
void Disambiguator::enterScopeInternal(Scope& _scope)
|
||||
{
|
||||
m_scopes.push_back(&_scope);
|
||||
}
|
||||
|
||||
void Disambiguator::leaveScopeInternal(Scope& _scope)
|
||||
{
|
||||
solAssert(!m_scopes.empty(), "");
|
||||
solAssert(m_scopes.back() == &_scope, "");
|
||||
m_scopes.pop_back();
|
||||
}
|
67
libjulia/optimiser/Disambiguator.h
Normal file
67
libjulia/optimiser/Disambiguator.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that makes all identifiers unique.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libjulia/optimiser/ASTCopier.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Creates a copy of a iulia AST replacing all identifiers by unique names.
|
||||
*/
|
||||
class Disambiguator: public ASTCopier
|
||||
{
|
||||
public:
|
||||
Disambiguator(solidity::assembly::AsmAnalysisInfo const& _analysisInfo):
|
||||
m_info(_analysisInfo)
|
||||
{}
|
||||
|
||||
protected:
|
||||
virtual void enterScope(Block const& _block) override;
|
||||
virtual void leaveScope(Block const& _block) override;
|
||||
virtual void enterFunction(FunctionDefinition const& _function) override;
|
||||
virtual void leaveFunction(FunctionDefinition const& _function) override;
|
||||
virtual std::string translateIdentifier(std::string const& _name) override;
|
||||
|
||||
void enterScopeInternal(solidity::assembly::Scope& _scope);
|
||||
void leaveScopeInternal(solidity::assembly::Scope& _scope);
|
||||
|
||||
solidity::assembly::AsmAnalysisInfo const& m_info;
|
||||
|
||||
std::vector<solidity::assembly::Scope*> m_scopes;
|
||||
std::map<void const*, std::string> m_translations;
|
||||
std::set<std::string> m_usedNames;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
74
libjulia/optimiser/ExpressionInliner.cpp
Normal file
74
libjulia/optimiser/ExpressionInliner.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that performs function inlining inside expressions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/ExpressionInliner.h>
|
||||
|
||||
#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h>
|
||||
#include <libjulia/optimiser/Substitution.h>
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/all_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
void ExpressionInliner::run()
|
||||
{
|
||||
InlinableExpressionFunctionFinder funFinder;
|
||||
funFinder(m_block);
|
||||
m_inlinableFunctions = funFinder.inlinableFunctions();
|
||||
|
||||
(*this)(m_block);
|
||||
}
|
||||
|
||||
|
||||
void ExpressionInliner::operator()(FunctionDefinition& _fun)
|
||||
{
|
||||
ASTModifier::operator()(_fun);
|
||||
}
|
||||
|
||||
void ExpressionInliner::visit(Expression& _expression)
|
||||
{
|
||||
ASTModifier::visit(_expression);
|
||||
if (_expression.type() == typeid(FunctionCall))
|
||||
{
|
||||
FunctionCall& funCall = boost::get<FunctionCall>(_expression);
|
||||
|
||||
bool movable = boost::algorithm::all_of(
|
||||
funCall.arguments,
|
||||
[=](Expression const& _arg) { return MovableChecker(_arg).movable(); }
|
||||
);
|
||||
if (m_inlinableFunctions.count(funCall.functionName.name) && movable)
|
||||
{
|
||||
FunctionDefinition const& fun = *m_inlinableFunctions.at(funCall.functionName.name);
|
||||
map<string, Expression const*> substitutions;
|
||||
for (size_t i = 0; i < fun.parameters.size(); ++i)
|
||||
substitutions[fun.parameters[i].name] = &funCall.arguments[i];
|
||||
_expression = Substitution(substitutions).translate(*boost::get<Assignment>(fun.body.statements.front()).value);
|
||||
|
||||
// TODO Add metric! This metric should perform well on a pair of functions who
|
||||
// call each other recursively.
|
||||
}
|
||||
}
|
||||
}
|
74
libjulia/optimiser/ExpressionInliner.h
Normal file
74
libjulia/optimiser/ExpressionInliner.h
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that performs function inlining.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Optimiser component that modifies an AST in place, inlining functions that can be
|
||||
* inlined inside functional expressions, i.e. functions that
|
||||
* - return a single value
|
||||
* - have a body like r := <functional expression>
|
||||
* - neither reference themselves nor r in the right hand side
|
||||
*
|
||||
* Furthermore, the arguments of the function call cannot have any side-effects.
|
||||
*
|
||||
* This component can only be used on sources with unique names.
|
||||
*/
|
||||
class ExpressionInliner: public ASTModifier
|
||||
{
|
||||
public:
|
||||
ExpressionInliner(Block& _block):
|
||||
m_block(_block)
|
||||
{}
|
||||
|
||||
void run();
|
||||
|
||||
using ASTModifier::operator();
|
||||
virtual void operator()(FunctionDefinition& _fun) override;
|
||||
|
||||
virtual void visit(Expression& _expression) override;
|
||||
|
||||
private:
|
||||
std::map<std::string, FunctionDefinition const*> m_inlinableFunctions;
|
||||
std::map<std::string, std::string> m_varReplacements;
|
||||
/// Set of functions we are currently visiting inside.
|
||||
std::set<std::string> m_currentFunctions;
|
||||
|
||||
Block& m_block;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
50
libjulia/optimiser/ExpressionSimplifier.cpp
Normal file
50
libjulia/optimiser/ExpressionSimplifier.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that uses the simplification rules to simplify expressions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/ExpressionSimplifier.h>
|
||||
|
||||
#include <libjulia/optimiser/SimplificationRules.h>
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
|
||||
void ExpressionSimplifier::visit(Expression& _expression)
|
||||
{
|
||||
ASTModifier::visit(_expression);
|
||||
while (auto match = SimplificationRules::findFirstMatch(_expression))
|
||||
{
|
||||
// Do not apply the rule if it removes non-constant parts of the expression.
|
||||
// TODO: The check could actually be less strict than "movable".
|
||||
// We only require "Does not cause side-effects".
|
||||
if (match->removesNonConstants && !MovableChecker(_expression).movable())
|
||||
return;
|
||||
_expression = match->action().toExpression(locationOf(_expression));
|
||||
}
|
||||
}
|
45
libjulia/optimiser/ExpressionSimplifier.h
Normal file
45
libjulia/optimiser/ExpressionSimplifier.h
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that uses the simplification rules to simplify expressions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Applies simplification rules to all expressions.
|
||||
*/
|
||||
class ExpressionSimplifier: public ASTModifier
|
||||
{
|
||||
public:
|
||||
using ASTModifier::operator();
|
||||
virtual void visit(Expression& _expression);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
}
|
49
libjulia/optimiser/FunctionGrouper.cpp
Normal file
49
libjulia/optimiser/FunctionGrouper.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that changes the code of a block so that all non-function definition
|
||||
* statements are moved to a block of their own followed by all function definitions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/FunctionGrouper.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
|
||||
void FunctionGrouper::operator()(Block& _block)
|
||||
{
|
||||
vector<Statement> reordered;
|
||||
reordered.emplace_back(Block{_block.location, {}});
|
||||
|
||||
for (auto&& statement: _block.statements)
|
||||
{
|
||||
if (statement.type() == typeid(FunctionDefinition))
|
||||
reordered.emplace_back(std::move(statement));
|
||||
else
|
||||
boost::get<Block>(reordered.front()).statements.emplace_back(std::move(statement));
|
||||
}
|
||||
_block.statements = std::move(reordered);
|
||||
}
|
46
libjulia/optimiser/FunctionGrouper.h
Normal file
46
libjulia/optimiser/FunctionGrouper.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that changes the code of a black so that all non-function definition
|
||||
* instructions are moved to a block of their own followed by all function definitions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Moves all instructions in a block into a new block at the start of the block, followed by
|
||||
* all function definitions.
|
||||
*
|
||||
* After this step, a block is of the form
|
||||
* { { I...} F... }
|
||||
* Where I are (non-function-definition) instructions and F are function definitions.
|
||||
*/
|
||||
class FunctionGrouper
|
||||
{
|
||||
public:
|
||||
void operator()(Block& _block);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
53
libjulia/optimiser/FunctionHoister.cpp
Normal file
53
libjulia/optimiser/FunctionHoister.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that changes the code so that it consists of a block starting with
|
||||
* a single block followed only by function definitions and with no functions defined
|
||||
* anywhere else.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/FunctionHoister.h>
|
||||
#include <libjulia/optimiser/Utilities.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
using namespace dev::solidity;
|
||||
|
||||
void FunctionHoister::operator()(Block& _block)
|
||||
{
|
||||
bool topLevel = m_isTopLevel;
|
||||
m_isTopLevel = false;
|
||||
for (auto&& statement: _block.statements)
|
||||
{
|
||||
boost::apply_visitor(*this, statement);
|
||||
if (statement.type() == typeid(FunctionDefinition))
|
||||
{
|
||||
m_functions.emplace_back(std::move(statement));
|
||||
statement = Block{_block.location, {}};
|
||||
}
|
||||
}
|
||||
removeEmptyBlocks(_block);
|
||||
if (topLevel)
|
||||
_block.statements += std::move(m_functions);
|
||||
}
|
52
libjulia/optimiser/FunctionHoister.h
Normal file
52
libjulia/optimiser/FunctionHoister.h
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that changes the code so that all function definitions are at the top
|
||||
* level block.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Moves all functions to the top-level scope.
|
||||
* Applying this transformation to source code that has ambiguous identifiers might
|
||||
* lead to invalid code.
|
||||
*
|
||||
* Prerequisites: Disambiguator
|
||||
*/
|
||||
class FunctionHoister: public ASTModifier
|
||||
{
|
||||
public:
|
||||
using ASTModifier::operator();
|
||||
virtual void operator()(Block& _block);
|
||||
|
||||
private:
|
||||
bool m_isTopLevel = true;
|
||||
std::vector<Statement> m_functions;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
70
libjulia/optimiser/InlinableExpressionFunctionFinder.cpp
Normal file
70
libjulia/optimiser/InlinableExpressionFunctionFinder.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that identifies functions to be inlined.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/InlinableExpressionFunctionFinder.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
void InlinableExpressionFunctionFinder::operator()(Identifier const& _identifier)
|
||||
{
|
||||
checkAllowed(_identifier.name);
|
||||
ASTWalker::operator()(_identifier);
|
||||
}
|
||||
|
||||
void InlinableExpressionFunctionFinder::operator()(FunctionCall const& _funCall)
|
||||
{
|
||||
checkAllowed(_funCall.functionName.name);
|
||||
ASTWalker::operator()(_funCall);
|
||||
}
|
||||
|
||||
void InlinableExpressionFunctionFinder::operator()(FunctionDefinition const& _function)
|
||||
{
|
||||
if (_function.returnVariables.size() == 1 && _function.body.statements.size() == 1)
|
||||
{
|
||||
string const& retVariable = _function.returnVariables.front().name;
|
||||
Statement const& bodyStatement = _function.body.statements.front();
|
||||
if (bodyStatement.type() == typeid(Assignment))
|
||||
{
|
||||
Assignment const& assignment = boost::get<Assignment>(bodyStatement);
|
||||
if (assignment.variableNames.size() == 1 && assignment.variableNames.front().name == retVariable)
|
||||
{
|
||||
// FIXME: use code size metric here
|
||||
|
||||
// We cannot overwrite previous settings, because this function definition
|
||||
// would not be valid here if we were searching inside a functionally inlinable
|
||||
// function body.
|
||||
solAssert(m_disallowedIdentifiers.empty() && !m_foundDisallowedIdentifier, "");
|
||||
m_disallowedIdentifiers = set<string>{retVariable, _function.name};
|
||||
boost::apply_visitor(*this, *assignment.value);
|
||||
if (!m_foundDisallowedIdentifier)
|
||||
m_inlinableFunctions[_function.name] = &_function;
|
||||
m_disallowedIdentifiers.clear();
|
||||
m_foundDisallowedIdentifier = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ASTWalker::operator()(_function.body);
|
||||
}
|
71
libjulia/optimiser/InlinableExpressionFunctionFinder.h
Normal file
71
libjulia/optimiser/InlinableExpressionFunctionFinder.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimiser component that identifies functions to be inlined.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Optimiser component that finds functions that can be
|
||||
* inlined inside functional expressions, i.e. functions that
|
||||
* - have a single return parameter r
|
||||
* - have a body like r := <functional expression>
|
||||
* - neither reference themselves nor r in the right hand side
|
||||
*
|
||||
* This component can only be used on sources with unique names.
|
||||
*/
|
||||
class InlinableExpressionFunctionFinder: public ASTWalker
|
||||
{
|
||||
public:
|
||||
|
||||
std::map<std::string, FunctionDefinition const*> const& inlinableFunctions() const
|
||||
{
|
||||
return m_inlinableFunctions;
|
||||
}
|
||||
|
||||
using ASTWalker::operator();
|
||||
virtual void operator()(Identifier const& _identifier) override;
|
||||
virtual void operator()(FunctionCall const& _funCall) override;
|
||||
virtual void operator()(FunctionDefinition const& _function) override;
|
||||
|
||||
private:
|
||||
void checkAllowed(std::string const& _name)
|
||||
{
|
||||
if (m_disallowedIdentifiers.count(_name))
|
||||
m_foundDisallowedIdentifier = true;
|
||||
}
|
||||
|
||||
bool m_foundDisallowedIdentifier = false;
|
||||
std::set<std::string> m_disallowedIdentifiers;
|
||||
std::map<std::string, FunctionDefinition const*> m_inlinableFunctions;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
52
libjulia/optimiser/Metrics.cpp
Normal file
52
libjulia/optimiser/Metrics.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Module providing metrics for the optimizer.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/Metrics.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
size_t CodeSize::codeSize(Statement const& _statement)
|
||||
{
|
||||
CodeSize cs;
|
||||
cs.visit(_statement);
|
||||
return cs.m_size;
|
||||
}
|
||||
|
||||
size_t CodeSize::codeSize(Expression const& _expression)
|
||||
{
|
||||
CodeSize cs;
|
||||
cs.visit(_expression);
|
||||
return cs.m_size;
|
||||
}
|
||||
|
||||
void CodeSize::visit(Statement const& _statement)
|
||||
{
|
||||
++m_size;
|
||||
ASTWalker::visit(_statement);
|
||||
}
|
||||
|
||||
void CodeSize::visit(Expression const& _expression)
|
||||
{
|
||||
++m_size;
|
||||
ASTWalker::visit(_expression);
|
||||
}
|
49
libjulia/optimiser/Metrics.h
Normal file
49
libjulia/optimiser/Metrics.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Module providing metrics for the optimizer.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
class CodeSize: public ASTWalker
|
||||
{
|
||||
public:
|
||||
/// Returns a metric for the code size of an AST element.
|
||||
/// More specifically, it returns the number of AST nodes.
|
||||
static size_t codeSize(Statement const& _statement);
|
||||
/// Returns a metric for the code size of an AST element.
|
||||
/// More specifically, it returns the number of AST nodes.
|
||||
static size_t codeSize(Expression const& _expression);
|
||||
|
||||
private:
|
||||
virtual void visit(Statement const& _statement) override;
|
||||
virtual void visit(Expression const& _expression) override;
|
||||
|
||||
private:
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
75
libjulia/optimiser/NameCollector.cpp
Normal file
75
libjulia/optimiser/NameCollector.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Specific AST walker that collects all defined names.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/NameCollector.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
void NameCollector::operator()(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
for (auto const& var: _varDecl.variables)
|
||||
m_names.insert(var.name);
|
||||
}
|
||||
|
||||
void NameCollector::operator ()(FunctionDefinition const& _funDef)
|
||||
{
|
||||
m_names.insert(_funDef.name);
|
||||
m_functions[_funDef.name] = &_funDef;
|
||||
for (auto const arg: _funDef.parameters)
|
||||
m_names.insert(arg.name);
|
||||
for (auto const ret: _funDef.returnVariables)
|
||||
m_names.insert(ret.name);
|
||||
ASTWalker::operator ()(_funDef);
|
||||
}
|
||||
|
||||
void ReferencesCounter::operator()(Identifier const& _identifier)
|
||||
{
|
||||
++m_references[_identifier.name];
|
||||
}
|
||||
|
||||
void ReferencesCounter::operator()(FunctionCall const& _funCall)
|
||||
{
|
||||
++m_references[_funCall.functionName.name];
|
||||
ASTWalker::operator()(_funCall);
|
||||
}
|
||||
|
||||
map<string, size_t> ReferencesCounter::countReferences(Block const& _block)
|
||||
{
|
||||
ReferencesCounter counter;
|
||||
counter(_block);
|
||||
return counter.references();
|
||||
}
|
||||
|
||||
map<string, size_t> ReferencesCounter::countReferences(Expression const& _expression)
|
||||
{
|
||||
ReferencesCounter counter;
|
||||
counter.visit(_expression);
|
||||
return counter.references();
|
||||
}
|
||||
|
||||
void Assignments::operator()(Assignment const& _assignment)
|
||||
{
|
||||
for (auto const& var: _assignment.variableNames)
|
||||
m_names.insert(var.name);
|
||||
}
|
84
libjulia/optimiser/NameCollector.h
Normal file
84
libjulia/optimiser/NameCollector.h
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Specific AST walkers that collect facts about identifiers and definitions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Specific AST walker that collects all defined names.
|
||||
*/
|
||||
class NameCollector: public ASTWalker
|
||||
{
|
||||
public:
|
||||
using ASTWalker::operator ();
|
||||
virtual void operator()(VariableDeclaration const& _varDecl) override;
|
||||
virtual void operator()(FunctionDefinition const& _funDef) override;
|
||||
|
||||
std::set<std::string> const& names() const { return m_names; }
|
||||
std::map<std::string, FunctionDefinition const*> const& functions() const { return m_functions; }
|
||||
private:
|
||||
std::set<std::string> m_names;
|
||||
std::map<std::string, FunctionDefinition const*> m_functions;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specific AST walker that counts all references to all declarations.
|
||||
*/
|
||||
class ReferencesCounter: public ASTWalker
|
||||
{
|
||||
public:
|
||||
using ASTWalker::operator ();
|
||||
virtual void operator()(Identifier const& _identifier);
|
||||
virtual void operator()(FunctionCall const& _funCall);
|
||||
|
||||
static std::map<std::string, size_t> countReferences(Block const& _block);
|
||||
static std::map<std::string, size_t> countReferences(Expression const& _expression);
|
||||
|
||||
std::map<std::string, size_t> const& references() const { return m_references; }
|
||||
private:
|
||||
std::map<std::string, size_t> m_references;
|
||||
};
|
||||
|
||||
/**
|
||||
* Specific AST walker that finds all variables that are assigned to.
|
||||
*/
|
||||
class Assignments: public ASTWalker
|
||||
{
|
||||
public:
|
||||
using ASTWalker::operator ();
|
||||
virtual void operator()(Assignment const& _assignment) override;
|
||||
|
||||
std::set<std::string> const& names() const { return m_names; }
|
||||
private:
|
||||
std::set<std::string> m_names;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
89
libjulia/optimiser/README.md
Normal file
89
libjulia/optimiser/README.md
Normal file
@ -0,0 +1,89 @@
|
||||
## IULIA Optimiser
|
||||
|
||||
The iulia optimiser consists of several stages and components that all transform
|
||||
the AST in a semantically equivalent way. The goal is to end up either with code
|
||||
that is shorter or at least only marginally longer but will allow further
|
||||
optimisation steps.
|
||||
|
||||
The optimiser currently follows a purely greedy strategy and does not do any
|
||||
backtracking.
|
||||
|
||||
## Disambiguator
|
||||
|
||||
The disambiguator takes an AST and returns a fresh copy where all identifiers have
|
||||
names unique to the input AST. This is a prerequisite for all other optimiser stages.
|
||||
One of the benefits is that identifier lookup does not need to take scopes into account
|
||||
and we can basically ignore the result of the analysis phase.
|
||||
|
||||
All subsequent stages have the property that all names stay unique. This means if
|
||||
a new identifier needs to be introduced, a new unique name is generated.
|
||||
|
||||
## Function Hoister
|
||||
|
||||
The function hoister moves all function definitions to the topmost block. This is
|
||||
a semantically equivalent transformation as long as it is performed after the
|
||||
disambiguation stage. The reason is that moving a definition upwards cannot decrease
|
||||
its visibility and it is impossible to reference variables defined in a different function.
|
||||
|
||||
The benefit of this stage is that function definitions can be lookup up more easily.
|
||||
|
||||
## Function Grouper
|
||||
|
||||
The function grouper has to be applied after the disambiguator and the function hoister.
|
||||
Its effect is that all topmost elements that are not function definitions are moved
|
||||
into a single block which is the first satement of the root block.
|
||||
|
||||
After this step, a program has the following normal form:
|
||||
|
||||
{ I F... }
|
||||
|
||||
Where I is a block that does not contain any function definitions (not even recursively)
|
||||
and F is a list of function definitions such that no function contains a function definition.
|
||||
|
||||
## Functional Inliner
|
||||
|
||||
The functional inliner depends on the disambiguator, the function hoister and function grouper.
|
||||
It performs function inlining such that the result of the inlining is an expression. This can
|
||||
only be done if the body of the function to be inlined has the form ``{ r := E }`` where ``r``
|
||||
is the single return value of the function, ``E`` is an expression and all arguments in the
|
||||
function call are so-called movable expressions. A movable expression is either a literal, a
|
||||
variable or a function call (or EVM opcode) which does not have side-effects and also does not
|
||||
depend on any side-effects.
|
||||
|
||||
As an example, neither ``mload`` nor ``mstore`` would be allowed.
|
||||
|
||||
## Full Function Inliner
|
||||
|
||||
## Rematerialisation
|
||||
|
||||
The rematerialisation stage tries to replace variable references by the expression that
|
||||
was last assigned to the variable. This is of course only beneficial if this expression
|
||||
is comparatively cheap to evaluate. Furthermore, it is only semantically equivalent if
|
||||
the value of the expression did not change between the point of assignment and the
|
||||
point of use. The main benefit of this stage is that it can save stack slots if it
|
||||
leads to a variable being eliminated completely (see below), but it can also
|
||||
save a DUP opcode on the EVM if the expression is very cheap.
|
||||
|
||||
The algorithm only allows movable expressions (see above for a definition) in this case.
|
||||
Expressions that contain other variables are also disallowed if one of those variables
|
||||
have been assigned to in the meantime. This is also not applied to variables where
|
||||
assignment and use span across loops and conditionals.
|
||||
|
||||
## Unused Definition Pruner
|
||||
|
||||
If a variable or function is not referenced, it is removed from the code.
|
||||
If there are two assignments to a variable where the first one is a movable expression
|
||||
and the variable is not used between the two assignments (and the second is not inside
|
||||
a loop or conditional, the first one is not inside), the first assignment is removed.
|
||||
|
||||
|
||||
## Function Unifier
|
||||
|
||||
## Expression Simplifier
|
||||
|
||||
This step can only be applied for the EVM-flavoured dialect of iulia. It applies
|
||||
simple rules like ``x + 0 == x`` to simplify expressions.
|
||||
|
||||
## Ineffective Statement Remover
|
||||
|
||||
This step removes statements that have no side-effects.
|
54
libjulia/optimiser/Rematerialiser.cpp
Normal file
54
libjulia/optimiser/Rematerialiser.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimisation stage that replaces variables by their most recently assigned expressions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/Rematerialiser.h>
|
||||
|
||||
#include <libjulia/optimiser/Metrics.h>
|
||||
#include <libjulia/optimiser/ASTCopier.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
void Rematerialiser::visit(Expression& _e)
|
||||
{
|
||||
if (_e.type() == typeid(Identifier))
|
||||
{
|
||||
Identifier& identifier = boost::get<Identifier>(_e);
|
||||
if (m_value.count(identifier.name))
|
||||
{
|
||||
string name = identifier.name;
|
||||
bool expressionValid = true;
|
||||
for (auto const& ref: m_references[name])
|
||||
if (!inScope(ref))
|
||||
{
|
||||
expressionValid = false;
|
||||
break;
|
||||
}
|
||||
solAssert(m_value.at(name), "");
|
||||
auto const& value = *m_value.at(name);
|
||||
if (expressionValid && CodeSize::codeSize(value) <= 7)
|
||||
_e = (ASTCopier{}).translate(value);
|
||||
}
|
||||
}
|
||||
DataFlowAnalyzer::visit(_e);
|
||||
}
|
48
libjulia/optimiser/Rematerialiser.h
Normal file
48
libjulia/optimiser/Rematerialiser.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimisation stage that replaces variables by their most recently assigned expressions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/DataFlowAnalyzer.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Optimisation stage that replaces variables by their most recently assigned expressions.
|
||||
*
|
||||
* Prerequisite: Disambiguator
|
||||
*/
|
||||
class Rematerialiser: public DataFlowAnalyzer
|
||||
{
|
||||
protected:
|
||||
using ASTModifier::visit;
|
||||
virtual void visit(Expression& _e) override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
60
libjulia/optimiser/Semantics.cpp
Normal file
60
libjulia/optimiser/Semantics.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Specific AST walkers that collect semantical facts.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
MovableChecker::MovableChecker(Expression const& _expression)
|
||||
{
|
||||
visit(_expression);
|
||||
}
|
||||
|
||||
void MovableChecker::operator()(Identifier const& _identifier)
|
||||
{
|
||||
ASTWalker::operator()(_identifier);
|
||||
m_variableReferences.insert(_identifier.name);
|
||||
}
|
||||
|
||||
void MovableChecker::operator()(FunctionalInstruction const& _instr)
|
||||
{
|
||||
if (!eth::SemanticInformation::movable(_instr.instruction))
|
||||
m_movable = false;
|
||||
else
|
||||
ASTWalker::operator()(_instr);
|
||||
}
|
||||
|
||||
void MovableChecker::operator()(FunctionCall const&)
|
||||
{
|
||||
m_movable = false;
|
||||
}
|
||||
|
||||
void MovableChecker::visit(Statement const&)
|
||||
{
|
||||
solAssert(false, "Movability for statement requested.");
|
||||
}
|
62
libjulia/optimiser/Semantics.h
Normal file
62
libjulia/optimiser/Semantics.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Specific AST walkers that collect semantical facts.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Specific AST walker that determines whether an expression is movable.
|
||||
*/
|
||||
class MovableChecker: public ASTWalker
|
||||
{
|
||||
public:
|
||||
MovableChecker() = default;
|
||||
explicit MovableChecker(Expression const& _expression);
|
||||
|
||||
virtual void operator()(Identifier const& _identifier) override;
|
||||
virtual void operator()(FunctionalInstruction const& _functionalInstruction) override;
|
||||
virtual void operator()(FunctionCall const& _functionCall) override;
|
||||
|
||||
/// Disallow visiting anything apart from Expressions (this throws).
|
||||
virtual void visit(Statement const&) override;
|
||||
using ASTWalker::visit;
|
||||
|
||||
bool movable() const { return m_movable; }
|
||||
std::set<std::string> const& referencedVariables() const { return m_variableReferences; }
|
||||
|
||||
private:
|
||||
/// Which variables the current expression references.
|
||||
std::set<std::string> m_variableReferences;
|
||||
/// Is the current expression movable or not.
|
||||
bool m_movable = true;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
182
libjulia/optimiser/SimplificationRules.cpp
Normal file
182
libjulia/optimiser/SimplificationRules.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Module for applying replacement rules against Expressions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/SimplificationRules.h>
|
||||
|
||||
#include <libjulia/optimiser/Utilities.h>
|
||||
#include <libjulia/optimiser/ASTCopier.h>
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
#include <libjulia/optimiser/SyntacticalEquality.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libevmasm/RuleList.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
|
||||
SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch(Expression const& _expr)
|
||||
{
|
||||
if (_expr.type() != typeid(FunctionalInstruction))
|
||||
return nullptr;
|
||||
|
||||
static SimplificationRules rules;
|
||||
|
||||
FunctionalInstruction const& instruction = boost::get<FunctionalInstruction const&>(_expr);
|
||||
for (auto const& rule: rules.m_rules[byte(instruction.instruction)])
|
||||
{
|
||||
rules.resetMatchGroups();
|
||||
if (rule.pattern.matches(_expr))
|
||||
return &rule;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules)
|
||||
{
|
||||
for (auto const& r: _rules)
|
||||
addRule(r);
|
||||
}
|
||||
|
||||
void SimplificationRules::addRule(SimplificationRule<Pattern> const& _rule)
|
||||
{
|
||||
m_rules[byte(_rule.pattern.instruction())].push_back(_rule);
|
||||
}
|
||||
|
||||
SimplificationRules::SimplificationRules()
|
||||
{
|
||||
// Multiple occurences of one of these inside one rule must match the same equivalence class.
|
||||
// Constants.
|
||||
Pattern A(PatternKind::Constant);
|
||||
Pattern B(PatternKind::Constant);
|
||||
Pattern C(PatternKind::Constant);
|
||||
// Anything.
|
||||
Pattern X;
|
||||
Pattern Y;
|
||||
A.setMatchGroup(1, m_matchGroups);
|
||||
B.setMatchGroup(2, m_matchGroups);
|
||||
C.setMatchGroup(3, m_matchGroups);
|
||||
X.setMatchGroup(4, m_matchGroups);
|
||||
Y.setMatchGroup(5, m_matchGroups);
|
||||
|
||||
addRules(simplificationRuleList(A, B, C, X, Y));
|
||||
}
|
||||
|
||||
Pattern::Pattern(solidity::Instruction _instruction, vector<Pattern> const& _arguments):
|
||||
m_kind(PatternKind::Operation),
|
||||
m_instruction(_instruction),
|
||||
m_arguments(_arguments)
|
||||
{
|
||||
}
|
||||
|
||||
void Pattern::setMatchGroup(unsigned _group, map<unsigned, Expression const*>& _matchGroups)
|
||||
{
|
||||
m_matchGroup = _group;
|
||||
m_matchGroups = &_matchGroups;
|
||||
}
|
||||
|
||||
bool Pattern::matches(Expression const& _expr) const
|
||||
{
|
||||
if (m_kind == PatternKind::Constant)
|
||||
{
|
||||
if (_expr.type() != typeid(Literal))
|
||||
return false;
|
||||
Literal const& literal = boost::get<Literal const&>(_expr);
|
||||
if (literal.kind != assembly::LiteralKind::Number)
|
||||
return false;
|
||||
if (m_data && *m_data != u256(literal.value))
|
||||
return false;
|
||||
assertThrow(m_arguments.empty(), OptimizerException, "");
|
||||
}
|
||||
else if (m_kind == PatternKind::Operation)
|
||||
{
|
||||
if (_expr.type() != typeid(FunctionalInstruction))
|
||||
return false;
|
||||
FunctionalInstruction const& instr = boost::get<FunctionalInstruction const&>(_expr);
|
||||
if (m_instruction != instr.instruction)
|
||||
return false;
|
||||
assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, "");
|
||||
for (size_t i = 0; i < m_arguments.size(); ++i)
|
||||
if (!m_arguments[i].matches(instr.arguments.at(i)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
assertThrow(m_arguments.empty(), OptimizerException, "");
|
||||
}
|
||||
// We support matching multiple expressions that require the same value
|
||||
// based on identical ASTs, which have to be movable.
|
||||
if (m_matchGroup)
|
||||
{
|
||||
if (m_matchGroups->count(m_matchGroup))
|
||||
{
|
||||
Expression const* firstMatch = (*m_matchGroups)[m_matchGroup];
|
||||
assertThrow(firstMatch, OptimizerException, "Match set but to null.");
|
||||
return
|
||||
SyntacticalEqualityChecker::equal(*firstMatch, _expr) &&
|
||||
MovableChecker(_expr).movable();
|
||||
}
|
||||
else
|
||||
(*m_matchGroups)[m_matchGroup] = &_expr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
solidity::Instruction Pattern::instruction() const
|
||||
{
|
||||
assertThrow(m_kind == PatternKind::Operation, OptimizerException, "");
|
||||
return m_instruction;
|
||||
}
|
||||
|
||||
Expression Pattern::toExpression(SourceLocation const& _location) const
|
||||
{
|
||||
if (matchGroup())
|
||||
return ASTCopier().translate(matchGroupValue());
|
||||
if (m_kind == PatternKind::Constant)
|
||||
{
|
||||
assertThrow(m_data, OptimizerException, "No match group and no constant value given.");
|
||||
return Literal{_location, assembly::LiteralKind::Number, formatNumber(*m_data), ""};
|
||||
}
|
||||
else if (m_kind == PatternKind::Operation)
|
||||
{
|
||||
vector<Expression> arguments;
|
||||
for (auto const& arg: m_arguments)
|
||||
arguments.emplace_back(arg.toExpression(_location));
|
||||
return FunctionalInstruction{_location, m_instruction, std::move(arguments)};
|
||||
}
|
||||
assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group.");
|
||||
}
|
||||
|
||||
u256 Pattern::d() const
|
||||
{
|
||||
Literal const& literal = boost::get<Literal const&>(matchGroupValue());
|
||||
assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, "");
|
||||
return u256(literal.value);
|
||||
}
|
||||
|
||||
Expression const& Pattern::matchGroupValue() const
|
||||
{
|
||||
assertThrow(m_matchGroup > 0, OptimizerException, "");
|
||||
assertThrow(!!m_matchGroups, OptimizerException, "");
|
||||
assertThrow((*m_matchGroups)[m_matchGroup], OptimizerException, "");
|
||||
return *(*m_matchGroups)[m_matchGroup];
|
||||
}
|
117
libjulia/optimiser/SimplificationRules.h
Normal file
117
libjulia/optimiser/SimplificationRules.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Module for applying replacement rules against Expressions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libevmasm/ExpressionClasses.h>
|
||||
#include <libevmasm/SimplificationRule.h>
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
class Pattern;
|
||||
|
||||
/**
|
||||
* Container for all simplification rules.
|
||||
*/
|
||||
class SimplificationRules: public boost::noncopyable
|
||||
{
|
||||
public:
|
||||
SimplificationRules();
|
||||
|
||||
/// @returns a pointer to the first matching pattern and sets the match
|
||||
/// groups accordingly.
|
||||
static SimplificationRule<Pattern> const* findFirstMatch(Expression const& _expr);
|
||||
|
||||
private:
|
||||
void addRules(std::vector<SimplificationRule<Pattern>> const& _rules);
|
||||
void addRule(SimplificationRule<Pattern> const& _rule);
|
||||
|
||||
void resetMatchGroups() { m_matchGroups.clear(); }
|
||||
|
||||
std::map<unsigned, Expression const*> m_matchGroups;
|
||||
std::vector<SimplificationRule<Pattern>> m_rules[256];
|
||||
};
|
||||
|
||||
enum class PatternKind
|
||||
{
|
||||
Operation,
|
||||
Constant,
|
||||
Any
|
||||
};
|
||||
|
||||
/**
|
||||
* Pattern to match against an expression.
|
||||
* Also stores matched expressions to retrieve them later, for constructing new expressions using
|
||||
* ExpressionTemplate.
|
||||
*/
|
||||
class Pattern
|
||||
{
|
||||
public:
|
||||
/// Matches any expression.
|
||||
Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {}
|
||||
// Matches a specific constant value.
|
||||
Pattern(unsigned _value): Pattern(u256(_value)) {}
|
||||
// Matches a specific constant value.
|
||||
Pattern(u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<u256>(_value)) {}
|
||||
// Matches a given instruction with given arguments
|
||||
Pattern(solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {});
|
||||
/// Sets this pattern to be part of the match group with the identifier @a _group.
|
||||
/// Inside one rule, all patterns in the same match group have to match expressions from the
|
||||
/// same expression equivalence class.
|
||||
void setMatchGroup(unsigned _group, std::map<unsigned, Expression const*>& _matchGroups);
|
||||
unsigned matchGroup() const { return m_matchGroup; }
|
||||
bool matches(Expression const& _expr) const;
|
||||
|
||||
std::vector<Pattern> arguments() const { return m_arguments; }
|
||||
|
||||
/// @returns the data of the matched expression if this pattern is part of a match group.
|
||||
u256 d() const;
|
||||
|
||||
solidity::Instruction instruction() const;
|
||||
|
||||
/// Turns this pattern into an actual expression. Should only be called
|
||||
/// for patterns resulting from an action, i.e. with match groups assigned.
|
||||
Expression toExpression(SourceLocation const& _location) const;
|
||||
|
||||
private:
|
||||
Expression const& matchGroupValue() const;
|
||||
|
||||
PatternKind m_kind = PatternKind::Any;
|
||||
solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation
|
||||
std::shared_ptr<u256> m_data; ///< Only valid if m_kind is Constant
|
||||
std::vector<Pattern> m_arguments;
|
||||
unsigned m_matchGroup = 0;
|
||||
std::map<unsigned, Expression const*>* m_matchGroups = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
39
libjulia/optimiser/Substitution.cpp
Normal file
39
libjulia/optimiser/Substitution.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Specific AST copier that replaces certain identifiers with expressions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/Substitution.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
Expression Substitution::translate(Expression const& _expression)
|
||||
{
|
||||
if (_expression.type() == typeid(Identifier))
|
||||
{
|
||||
string const& name = boost::get<Identifier>(_expression).name;
|
||||
if (m_substitutions.count(name))
|
||||
// No recursive substitution
|
||||
return ASTCopier().translate(*m_substitutions.at(name));
|
||||
}
|
||||
return ASTCopier::translate(_expression);
|
||||
}
|
50
libjulia/optimiser/Substitution.h
Normal file
50
libjulia/optimiser/Substitution.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Specific AST copier that replaces certain identifiers with expressions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTCopier.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Specific AST copier that replaces certain identifiers with expressions.
|
||||
*/
|
||||
class Substitution: public ASTCopier
|
||||
{
|
||||
public:
|
||||
Substitution(std::map<std::string, Expression const*> const& _substitutions):
|
||||
m_substitutions(_substitutions)
|
||||
{}
|
||||
virtual Expression translate(Expression const& _expression) override;
|
||||
|
||||
private:
|
||||
std::map<std::string, Expression const*> const& m_substitutions;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
75
libjulia/optimiser/SyntacticalEquality.cpp
Normal file
75
libjulia/optimiser/SyntacticalEquality.cpp
Normal file
@ -0,0 +1,75 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Component that can compare ASTs for equality on a syntactic basis.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/SyntacticalEquality.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
#include <libsolidity/interface/Exceptions.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2)
|
||||
{
|
||||
if (_e1.type() != _e2.type())
|
||||
return false;
|
||||
|
||||
// TODO This should be replaced by some kind of AST walker as soon as it gets
|
||||
// more complex.
|
||||
if (_e1.type() == typeid(FunctionalInstruction))
|
||||
{
|
||||
auto const& e1 = boost::get<FunctionalInstruction>(_e1);
|
||||
auto const& e2 = boost::get<FunctionalInstruction>(_e2);
|
||||
return
|
||||
e1.instruction == e2.instruction &&
|
||||
equalVector(e1.arguments, e2.arguments);
|
||||
}
|
||||
else if (_e1.type() == typeid(FunctionCall))
|
||||
{
|
||||
auto const& e1 = boost::get<FunctionCall>(_e1);
|
||||
auto const& e2 = boost::get<FunctionCall>(_e2);
|
||||
return
|
||||
equal(e1.functionName, e2.functionName) &&
|
||||
equalVector(e1.arguments, e2.arguments);
|
||||
}
|
||||
else if (_e1.type() == typeid(Identifier))
|
||||
return boost::get<Identifier>(_e1).name == boost::get<Identifier>(_e2).name;
|
||||
else if (_e1.type() == typeid(Literal))
|
||||
{
|
||||
auto const& e1 = boost::get<Literal>(_e1);
|
||||
auto const& e2 = boost::get<Literal>(_e2);
|
||||
return e1.kind == e2.kind && e1.value == e2.value && e1.type == e2.type;
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(false, "Invlid expression");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SyntacticalEqualityChecker::equalVector(vector<Expression> const& _e1, vector<Expression> const& _e2)
|
||||
{
|
||||
return _e1.size() == _e2.size() &&
|
||||
std::equal(begin(_e1), end(_e1), begin(_e2), SyntacticalEqualityChecker::equal);
|
||||
|
||||
}
|
50
libjulia/optimiser/SyntacticalEquality.h
Normal file
50
libjulia/optimiser/SyntacticalEquality.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Component that can compare ASTs for equality on a syntactic basis.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Component that can compare ASTs for equality on a syntactic basis.
|
||||
* Ignores source locations but requires exact matches otherwise.
|
||||
*
|
||||
* TODO: Only implemented for Expressions for now.
|
||||
* A future version might also recognize renamed variables and thus could be used to
|
||||
* remove duplicate functions.
|
||||
*/
|
||||
class SyntacticalEqualityChecker
|
||||
{
|
||||
public:
|
||||
static bool equal(Expression const& _e1, Expression const& _e2);
|
||||
|
||||
protected:
|
||||
static bool equalVector(std::vector<Expression> const& _e1, std::vector<Expression> const& _e2);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
116
libjulia/optimiser/UnusedPruner.cpp
Normal file
116
libjulia/optimiser/UnusedPruner.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimisation stage that removes unused variables and functions.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/UnusedPruner.h>
|
||||
|
||||
#include <libjulia/optimiser/NameCollector.h>
|
||||
#include <libjulia/optimiser/Semantics.h>
|
||||
#include <libjulia/optimiser/Utilities.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <boost/algorithm/cxx11/none_of.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
UnusedPruner::UnusedPruner(Block& _ast)
|
||||
{
|
||||
ReferencesCounter counter;
|
||||
counter(_ast);
|
||||
|
||||
m_references = counter.references();
|
||||
}
|
||||
|
||||
void UnusedPruner::operator()(Block& _block)
|
||||
{
|
||||
for (auto&& statement: _block.statements)
|
||||
if (statement.type() == typeid(FunctionDefinition))
|
||||
{
|
||||
FunctionDefinition& funDef = boost::get<FunctionDefinition>(statement);
|
||||
if (!used(funDef.name))
|
||||
{
|
||||
subtractReferences(ReferencesCounter::countReferences(funDef.body));
|
||||
statement = Block{std::move(funDef.location), {}};
|
||||
}
|
||||
}
|
||||
else if (statement.type() == typeid(VariableDeclaration))
|
||||
{
|
||||
VariableDeclaration& varDecl = boost::get<VariableDeclaration>(statement);
|
||||
// Multi-variable declarations are special. We can only remove it
|
||||
// if all vairables are unused and the right-hand-side is either
|
||||
// movable or it return a single value. In the latter case, we
|
||||
// replace `let a := f()` by `pop(f())` (in pure IULIA, this will be
|
||||
// `drop(f())`).
|
||||
if (boost::algorithm::none_of(
|
||||
varDecl.variables,
|
||||
[=](TypedName const& _typedName) { return used(_typedName.name); }
|
||||
))
|
||||
{
|
||||
if (!varDecl.value)
|
||||
statement = Block{std::move(varDecl.location), {}};
|
||||
else if (MovableChecker(*varDecl.value).movable())
|
||||
{
|
||||
subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
|
||||
statement = Block{std::move(varDecl.location), {}};
|
||||
}
|
||||
else if (varDecl.variables.size() == 1)
|
||||
// In pure IULIA, this should be replaced by a function call to `drop`
|
||||
// instead of `pop`.
|
||||
statement = ExpressionStatement{varDecl.location, FunctionalInstruction{
|
||||
varDecl.location,
|
||||
solidity::Instruction::POP,
|
||||
{*std::move(varDecl.value)}
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
removeEmptyBlocks(_block);
|
||||
|
||||
ASTModifier::operator()(_block);
|
||||
}
|
||||
|
||||
void UnusedPruner::runUntilStabilised(Block& _ast)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
UnusedPruner pruner(_ast);
|
||||
pruner(_ast);
|
||||
if (!pruner.shouldRunAgain())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnusedPruner::used(string const& _name) const
|
||||
{
|
||||
return m_references.count(_name) && m_references.at(_name) > 0;
|
||||
}
|
||||
|
||||
void UnusedPruner::subtractReferences(map<string, size_t> const& _subtrahend)
|
||||
{
|
||||
for (auto const& ref: _subtrahend)
|
||||
{
|
||||
solAssert(m_references.count(ref.first), "");
|
||||
solAssert(m_references.at(ref.first) >= ref.second, "");
|
||||
m_references[ref.first] -= ref.second;
|
||||
m_shouldRunAgain = true;
|
||||
}
|
||||
}
|
67
libjulia/optimiser/UnusedPruner.h
Normal file
67
libjulia/optimiser/UnusedPruner.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Optimisation stage that removes unused variables and functions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/optimiser/ASTWalker.h>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
/**
|
||||
* Optimisation stage that removes unused variables and functions.
|
||||
*
|
||||
* TODO: Also remove intermediate variable assignments from movable expressions
|
||||
* which are not referenced until after the next assignment to the same variable.
|
||||
*
|
||||
* Note that this does not remove circular references.
|
||||
*
|
||||
* Prerequisite: Disambiguator
|
||||
*/
|
||||
class UnusedPruner: public ASTModifier
|
||||
{
|
||||
public:
|
||||
explicit UnusedPruner(Block& _ast);
|
||||
|
||||
using ASTModifier::operator();
|
||||
virtual void operator()(Block& _block) override;
|
||||
|
||||
// @returns true iff the code changed in the previous run.
|
||||
bool shouldRunAgain() const { return m_shouldRunAgain; }
|
||||
|
||||
// Run the pruner until the code does not change anymore.
|
||||
static void runUntilStabilised(Block& _ast);
|
||||
|
||||
private:
|
||||
bool used(std::string const& _name) const;
|
||||
void subtractReferences(std::map<std::string, size_t> const& _subtrahend);
|
||||
|
||||
bool m_shouldRunAgain = false;
|
||||
std::map<std::string, size_t> m_references;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
39
libjulia/optimiser/Utilities.cpp
Normal file
39
libjulia/optimiser/Utilities.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
/*(
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Some useful snippets for the optimiser.
|
||||
*/
|
||||
|
||||
#include <libjulia/optimiser/Utilities.h>
|
||||
|
||||
#include <libsolidity/inlineasm/AsmData.h>
|
||||
|
||||
#include <libdevcore/CommonData.h>
|
||||
|
||||
#include <boost/range/algorithm_ext/erase.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::julia;
|
||||
|
||||
void dev::julia::removeEmptyBlocks(Block& _block)
|
||||
{
|
||||
auto isEmptyBlock = [](Statement const& _st) -> bool {
|
||||
return _st.type() == typeid(Block) && boost::get<Block>(_st).statements.empty();
|
||||
};
|
||||
boost::range::remove_erase_if(_block.statements, isEmptyBlock);
|
||||
}
|
39
libjulia/optimiser/Utilities.h
Normal file
39
libjulia/optimiser/Utilities.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Small useful snippets for the optimiser.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libjulia/ASTDataForward.h>
|
||||
|
||||
#include <libdevcore/Exceptions.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace julia
|
||||
{
|
||||
|
||||
struct IuliaException: virtual Exception {};
|
||||
struct OptimizerException: virtual IuliaException {};
|
||||
|
||||
/// Removes statements that are just empty blocks (non-recursive).
|
||||
void removeEmptyBlocks(Block& _block);
|
||||
|
||||
}
|
||||
}
|
9
libsolc/CMakeLists.txt
Normal file
9
libsolc/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
||||
if (EMSCRIPTEN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXPORTED_FUNCTIONS='[\"_compileJSON\",\"_license\",\"_version\",\"_compileJSONMulti\",\"_compileJSONCallback\",\"_compileStandard\"]' -s RESERVED_FUNCTION_POINTERS=20")
|
||||
add_executable(soljson libsolc.cpp)
|
||||
target_link_libraries(soljson PRIVATE solidity)
|
||||
else()
|
||||
add_library(libsolc libsolc.cpp)
|
||||
set_target_properties(libsolc PROPERTIES OUTPUT_NAME solc)
|
||||
target_link_libraries(libsolc PRIVATE solidity)
|
||||
endif()
|
@ -20,7 +20,7 @@
|
||||
* JSON interface for the solidity compiler to be used from Javascript.
|
||||
*/
|
||||
|
||||
#include <solc/jsonCompiler.h>
|
||||
#include <libsolc/libsolc.h>
|
||||
#include <libdevcore/Common.h>
|
||||
#include <libdevcore/JSON.h>
|
||||
#include <libsolidity/interface/StandardCompiler.h>
|
@ -28,51 +28,42 @@ using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation)
|
||||
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
||||
{
|
||||
TypePointer const& subType = _operation.subExpression().annotation().type;
|
||||
if (!dynamic_cast<RationalNumberType const*>(subType.get()))
|
||||
m_errorReporter.fatalTypeError(_operation.subExpression().location(), "Invalid constant expression.");
|
||||
TypePointer t = subType->unaryOperatorResult(_operation.getOperator());
|
||||
_operation.annotation().type = t;
|
||||
auto sub = type(_operation.subExpression());
|
||||
if (sub)
|
||||
setType(_operation, sub->unaryOperatorResult(_operation.getOperator()));
|
||||
}
|
||||
|
||||
/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation)
|
||||
void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
{
|
||||
TypePointer const& leftType = _operation.leftExpression().annotation().type;
|
||||
TypePointer const& rightType = _operation.rightExpression().annotation().type;
|
||||
if (!dynamic_cast<RationalNumberType const*>(leftType.get()))
|
||||
m_errorReporter.fatalTypeError(_operation.leftExpression().location(), "Invalid constant expression.");
|
||||
if (!dynamic_cast<RationalNumberType const*>(rightType.get()))
|
||||
m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression.");
|
||||
TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
|
||||
if (!commonType)
|
||||
auto left = type(_operation.leftExpression());
|
||||
auto right = type(_operation.rightExpression());
|
||||
if (left && right)
|
||||
{
|
||||
m_errorReporter.typeError(
|
||||
auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
|
||||
if (!commonType)
|
||||
m_errorReporter.fatalTypeError(
|
||||
_operation.location(),
|
||||
"Operator " +
|
||||
string(Token::toString(_operation.getOperator())) +
|
||||
" not compatible with types " +
|
||||
leftType->toString() +
|
||||
left->toString() +
|
||||
" and " +
|
||||
rightType->toString()
|
||||
right->toString()
|
||||
);
|
||||
commonType = leftType;
|
||||
}
|
||||
_operation.annotation().commonType = commonType;
|
||||
_operation.annotation().type =
|
||||
setType(
|
||||
_operation,
|
||||
Token::isCompareOp(_operation.getOperator()) ?
|
||||
make_shared<BoolType>() :
|
||||
commonType;
|
||||
commonType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantEvaluator::endVisit(Literal const& _literal)
|
||||
{
|
||||
_literal.annotation().type = Type::forLiteral(_literal);
|
||||
if (!_literal.annotation().type)
|
||||
m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value.");
|
||||
setType(_literal, Type::forLiteral(_literal));
|
||||
}
|
||||
|
||||
void ConstantEvaluator::endVisit(Identifier const& _identifier)
|
||||
@ -81,18 +72,34 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
|
||||
if (!variableDeclaration)
|
||||
return;
|
||||
if (!variableDeclaration->isConstant())
|
||||
m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant.");
|
||||
return;
|
||||
|
||||
ASTPointer<Expression> value = variableDeclaration->value();
|
||||
ASTPointer<Expression> const& value = variableDeclaration->value();
|
||||
if (!value)
|
||||
m_errorReporter.fatalTypeError(_identifier.location(), "Constant identifier declaration must have a constant value.");
|
||||
|
||||
if (!value->annotation().type)
|
||||
return;
|
||||
else if (!m_types->count(value.get()))
|
||||
{
|
||||
if (m_depth > 32)
|
||||
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
|
||||
ConstantEvaluator e(*value, m_errorReporter, m_depth + 1);
|
||||
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
|
||||
}
|
||||
|
||||
_identifier.annotation().type = value->annotation().type;
|
||||
setType(_identifier, type(*value));
|
||||
}
|
||||
|
||||
void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type)
|
||||
{
|
||||
if (_type && _type->category() == Type::Category::RationalNumber)
|
||||
(*m_types)[&_node] = _type;
|
||||
}
|
||||
|
||||
TypePointer ConstantEvaluator::type(ASTNode const& _node)
|
||||
{
|
||||
return (*m_types)[&_node];
|
||||
}
|
||||
|
||||
TypePointer ConstantEvaluator::evaluate(Expression const& _expr)
|
||||
{
|
||||
_expr.accept(*this);
|
||||
return type(_expr);
|
||||
}
|
||||
|
@ -38,22 +38,32 @@ class TypeChecker;
|
||||
class ConstantEvaluator: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter, size_t _newDepth = 0):
|
||||
ConstantEvaluator(
|
||||
ErrorReporter& _errorReporter,
|
||||
size_t _newDepth = 0,
|
||||
std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>()
|
||||
):
|
||||
m_errorReporter(_errorReporter),
|
||||
m_depth(_newDepth)
|
||||
m_depth(_newDepth),
|
||||
m_types(_types)
|
||||
{
|
||||
_expr.accept(*this);
|
||||
}
|
||||
|
||||
TypePointer evaluate(Expression const& _expr);
|
||||
|
||||
private:
|
||||
virtual void endVisit(BinaryOperation const& _operation);
|
||||
virtual void endVisit(UnaryOperation const& _operation);
|
||||
virtual void endVisit(Literal const& _literal);
|
||||
virtual void endVisit(Identifier const& _identifier);
|
||||
|
||||
void setType(ASTNode const& _node, TypePointer const& _type);
|
||||
TypePointer type(ASTNode const& _node);
|
||||
|
||||
ErrorReporter& m_errorReporter;
|
||||
/// Current recursion depth.
|
||||
size_t m_depth;
|
||||
size_t m_depth = 0;
|
||||
std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <libsolidity/analysis/DeclarationContainer.h>
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/Types.h>
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
@ -105,7 +106,7 @@ bool DeclarationContainer::registerDeclaration(
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
|
||||
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
|
||||
{
|
||||
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
||||
auto result = m_declarations.find(_name);
|
||||
@ -115,3 +116,22 @@ std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString cons
|
||||
return m_enclosingContainer->resolveName(_name, true);
|
||||
return vector<Declaration const*>({});
|
||||
}
|
||||
|
||||
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
|
||||
{
|
||||
static size_t const MAXIMUM_EDIT_DISTANCE = 2;
|
||||
|
||||
vector<ASTString> similar;
|
||||
|
||||
for (auto const& declaration: m_declarations)
|
||||
{
|
||||
string const& declarationName = declaration.first;
|
||||
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
|
||||
similar.push_back(declarationName);
|
||||
}
|
||||
|
||||
if (m_enclosingContainer)
|
||||
similar += m_enclosingContainer->similarNames(_name);
|
||||
|
||||
return similar;
|
||||
}
|
||||
|
@ -58,6 +58,10 @@ public:
|
||||
/// @returns whether declaration is valid, and if not also returns previous declaration.
|
||||
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
|
||||
|
||||
/// @returns existing declaration names similar to @a _name.
|
||||
/// Searches this and all parent containers.
|
||||
std::vector<ASTString> similarNames(ASTString const& _name) const;
|
||||
|
||||
private:
|
||||
ASTNode const* m_enclosingNode;
|
||||
DeclarationContainer const* m_enclosingContainer;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/analysis/TypeChecker.h>
|
||||
#include <libsolidity/interface/ErrorReporter.h>
|
||||
#include <libdevcore/StringUtils.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@ -425,6 +426,11 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMer
|
||||
return result;
|
||||
}
|
||||
|
||||
string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const
|
||||
{
|
||||
return quotedAlternativesList(m_currentScope->similarNames(_name));
|
||||
}
|
||||
|
||||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
|
||||
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
|
||||
ASTNode& _astRoot,
|
||||
|
@ -93,6 +93,9 @@ public:
|
||||
/// Generate and store warnings about variables that are named like instructions.
|
||||
void warnVariablesNamedLikeInstructions();
|
||||
|
||||
/// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
|
||||
std::string similarNameSuggestions(ASTString const& _name) const;
|
||||
|
||||
private:
|
||||
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
|
||||
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
|
||||
|
@ -47,7 +47,13 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
||||
if (declarations.empty())
|
||||
fatalDeclarationError(_identifier.location(), "Undeclared identifier.");
|
||||
{
|
||||
string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
|
||||
string errorMessage =
|
||||
"Undeclared identifier." +
|
||||
(suggestions.empty()? "": " Did you mean " + std::move(suggestions) + "?");
|
||||
declarationError(_identifier.location(), errorMessage);
|
||||
}
|
||||
else if (declarations.size() == 1)
|
||||
_identifier.annotation().referencedDeclaration = declarations.front();
|
||||
else
|
||||
@ -90,7 +96,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
|
||||
{
|
||||
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
|
||||
if (!declaration)
|
||||
fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
|
||||
{
|
||||
declarationError(_typeName.location(), "Identifier not found or not unique.");
|
||||
return;
|
||||
}
|
||||
|
||||
_typeName.annotation().referencedDeclaration = declaration;
|
||||
|
||||
@ -101,7 +110,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
|
||||
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
_typeName.annotation().type = make_shared<ContractType>(*contract);
|
||||
else
|
||||
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
||||
typeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
|
||||
@ -112,17 +121,25 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
|
||||
case VariableDeclaration::Visibility::External:
|
||||
break;
|
||||
default:
|
||||
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
|
||||
typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
|
||||
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
|
||||
{
|
||||
typeError(_typeName.location(), "Only external function types can be payable.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_typeName.visibility() == VariableDeclaration::Visibility::External)
|
||||
for (auto const& t: _typeName.parameterTypes() + _typeName.returnParameterTypes())
|
||||
{
|
||||
solAssert(t->annotation().type, "Type not set for parameter.");
|
||||
if (!t->annotation().type->canBeUsedExternally(false))
|
||||
fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
|
||||
{
|
||||
typeError(t->location(), "Internal type cannot be used for external function type.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_typeName.annotation().type = make_shared<FunctionType>(_typeName);
|
||||
@ -142,15 +159,21 @@ void ReferencesResolver::endVisit(Mapping const& _typeName)
|
||||
void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
|
||||
{
|
||||
TypePointer baseType = _typeName.baseType().annotation().type;
|
||||
if (!baseType)
|
||||
{
|
||||
solAssert(!m_errorReporter.errors().empty(), "");
|
||||
return;
|
||||
}
|
||||
if (baseType->storageBytes() == 0)
|
||||
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
|
||||
if (Expression const* length = _typeName.length())
|
||||
{
|
||||
if (!length->annotation().type)
|
||||
ConstantEvaluator e(*length, m_errorReporter);
|
||||
auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get());
|
||||
TypePointer lengthTypeGeneric = length->annotation().type;
|
||||
if (!lengthTypeGeneric)
|
||||
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
|
||||
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get());
|
||||
if (!lengthType || !lengthType->mobileType())
|
||||
fatalTypeError(length->location(), "Invalid array length, expected integer literal.");
|
||||
fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
|
||||
else if (lengthType->isFractional())
|
||||
fatalTypeError(length->location(), "Array with fractional length specified.");
|
||||
else if (lengthType->isNegative())
|
||||
@ -206,7 +229,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
|
||||
|
||||
// Will be re-generated later with correct information
|
||||
assembly::AsmAnalysisInfo analysisInfo;
|
||||
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, false, resolver).analyze(_inlineAssembly.operations());
|
||||
assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -321,17 +344,13 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
|
||||
type = ref->copyForLocation(typeLoc, isPointer);
|
||||
}
|
||||
else if (varLoc != Location::Default && !ref)
|
||||
fatalTypeError(_variable.location(), "Storage location can only be given for array or struct types.");
|
||||
|
||||
if (!type)
|
||||
fatalTypeError(_variable.location(), "Invalid type name.");
|
||||
|
||||
}
|
||||
else if (!_variable.canHaveAutoType())
|
||||
fatalTypeError(_variable.location(), "Explicit type needed.");
|
||||
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
||||
typeError(_variable.location(), "Storage location can only be given for array or struct types.");
|
||||
|
||||
_variable.annotation().type = type;
|
||||
}
|
||||
else if (!_variable.canHaveAutoType())
|
||||
typeError(_variable.location(), "Explicit type needed.");
|
||||
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
||||
}
|
||||
|
||||
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
|
||||
|
@ -224,3 +224,17 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SyntaxChecker::visit(VariableDeclaration const& _declaration)
|
||||
{
|
||||
bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
|
||||
|
||||
if (!_declaration.typeName())
|
||||
{
|
||||
if (v050)
|
||||
m_errorReporter.syntaxError(_declaration.location(), "Use of the \"var\" keyword is deprecated.");
|
||||
else
|
||||
m_errorReporter.warning(_declaration.location(), "Use of the \"var\" keyword is deprecated.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -69,6 +69,8 @@ private:
|
||||
virtual bool visit(FunctionDefinition const& _function) override;
|
||||
virtual bool visit(FunctionTypeName const& _node) override;
|
||||
|
||||
virtual bool visit(VariableDeclaration const& _declaration) override;
|
||||
|
||||
ErrorReporter& m_errorReporter;
|
||||
|
||||
/// Flag that indicates whether a function modifier actually contains '_'.
|
||||
|
@ -171,13 +171,7 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con
|
||||
ssl.append("Another declaration is here:", (*it)->location());
|
||||
|
||||
string msg = "More than one constructor defined.";
|
||||
size_t occurrences = ssl.infos.size();
|
||||
if (occurrences > 32)
|
||||
{
|
||||
ssl.infos.resize(32);
|
||||
msg += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences.";
|
||||
}
|
||||
|
||||
ssl.limitSize(msg);
|
||||
m_errorReporter.declarationError(
|
||||
functions[_contract.name()].front()->location(),
|
||||
ssl,
|
||||
@ -219,12 +213,7 @@ void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _defini
|
||||
|
||||
if (ssl.infos.size() > 0)
|
||||
{
|
||||
size_t occurrences = ssl.infos.size();
|
||||
if (occurrences > 32)
|
||||
{
|
||||
ssl.infos.resize(32);
|
||||
_message += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences.";
|
||||
}
|
||||
ssl.limitSize(_message);
|
||||
|
||||
m_errorReporter.declarationError(
|
||||
overloads[i]->location(),
|
||||
@ -570,6 +559,17 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
|
||||
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
|
||||
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
|
||||
if (
|
||||
_function.visibility() > FunctionDefinition::Visibility::Internal &&
|
||||
type(*var)->category() == Type::Category::Struct &&
|
||||
!type(*var)->dataStoredIn(DataLocation::Storage) &&
|
||||
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)
|
||||
)
|
||||
m_errorReporter.typeError(
|
||||
var->location(),
|
||||
"Structs are only supported in the new experimental ABI encoder. "
|
||||
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
|
||||
);
|
||||
|
||||
var->accept(*this);
|
||||
}
|
||||
@ -604,6 +604,8 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
||||
{
|
||||
if (_function.visibility() < FunctionDefinition::Visibility::Public)
|
||||
m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
|
||||
else if (_function.visibility() != FunctionDefinition::Visibility::External)
|
||||
m_errorReporter.warning(_function.location(), "Functions in interfaces should be declared external.");
|
||||
}
|
||||
if (_function.isConstructor())
|
||||
m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces.");
|
||||
@ -873,7 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
assembly::AsmAnalyzer analyzer(
|
||||
*_inlineAssembly.annotation().analysisInfo,
|
||||
m_errorReporter,
|
||||
false,
|
||||
assembly::AsmFlavour::Loose,
|
||||
identifierAccess
|
||||
);
|
||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||
@ -1060,7 +1062,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
||||
_statement.initialValue()->location(),
|
||||
"Invalid rational " +
|
||||
valueComponentType->toString() +
|
||||
" (absolute value too large or divison by zero)."
|
||||
" (absolute value too large or division by zero)."
|
||||
);
|
||||
else
|
||||
solAssert(false, "");
|
||||
@ -1551,8 +1553,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
|
||||
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
|
||||
{
|
||||
bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
|
||||
|
||||
string msg =
|
||||
"Wrong argument count for function call: " +
|
||||
"Wrong argument count for " +
|
||||
string(isStructConstructorCall ? "struct constructor" : "function call") +
|
||||
": " +
|
||||
toString(arguments.size()) +
|
||||
" arguments given but expected " +
|
||||
toString(parameterTypes.size()) +
|
||||
@ -1668,10 +1674,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
||||
SecondarySourceLocation ssl;
|
||||
for (auto function: contract->annotation().unimplementedFunctions)
|
||||
ssl.append("Missing implementation:", function->location());
|
||||
string msg = "Trying to create an instance of an abstract contract.";
|
||||
ssl.limitSize(msg);
|
||||
m_errorReporter.typeError(
|
||||
_newExpression.location(),
|
||||
ssl,
|
||||
"Trying to create an instance of an abstract contract."
|
||||
msg
|
||||
);
|
||||
}
|
||||
if (!contract->constructorIsPublic())
|
||||
|
@ -40,19 +40,20 @@ public:
|
||||
void operator()(assembly::Label const&) { }
|
||||
void operator()(assembly::Instruction const& _instruction)
|
||||
{
|
||||
if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction))
|
||||
m_reportMutability(StateMutability::NonPayable, _instruction.location);
|
||||
else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction))
|
||||
m_reportMutability(StateMutability::View, _instruction.location);
|
||||
checkInstruction(_instruction.location, _instruction.instruction);
|
||||
}
|
||||
void operator()(assembly::Literal const&) {}
|
||||
void operator()(assembly::Identifier const&) {}
|
||||
void operator()(assembly::FunctionalInstruction const& _instr)
|
||||
{
|
||||
(*this)(_instr.instruction);
|
||||
checkInstruction(_instr.location, _instr.instruction);
|
||||
for (auto const& arg: _instr.arguments)
|
||||
boost::apply_visitor(*this, arg);
|
||||
}
|
||||
void operator()(assembly::ExpressionStatement const& _expr)
|
||||
{
|
||||
boost::apply_visitor(*this, _expr.expression);
|
||||
}
|
||||
void operator()(assembly::StackAssignment const&) {}
|
||||
void operator()(assembly::Assignment const& _assignment)
|
||||
{
|
||||
@ -102,6 +103,13 @@ public:
|
||||
|
||||
private:
|
||||
std::function<void(StateMutability, SourceLocation const&)> m_reportMutability;
|
||||
void checkInstruction(SourceLocation _location, solidity::Instruction _instruction)
|
||||
{
|
||||
if (eth::SemanticInformation::invalidInViewFunctions(_instruction))
|
||||
m_reportMutability(StateMutability::NonPayable, _location);
|
||||
else if (eth::SemanticInformation::invalidInPureFunctions(_instruction))
|
||||
m_reportMutability(StateMutability::View, _location);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@ -267,6 +275,22 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
|
||||
reportMutability(mut, _functionCall.location());
|
||||
}
|
||||
|
||||
bool ViewPureChecker::visit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
// Catch the special case of `this.f.selector` which is a pure expression.
|
||||
ASTString const& member = _memberAccess.memberName();
|
||||
if (
|
||||
_memberAccess.expression().annotation().type->category() == Type::Category::Function &&
|
||||
member == "selector"
|
||||
)
|
||||
if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
|
||||
if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression()))
|
||||
if (exprInt->name() == "this")
|
||||
// Do not continue visiting.
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
StateMutability mutability = StateMutability::Pure;
|
||||
|
@ -56,6 +56,7 @@ private:
|
||||
virtual bool visit(ModifierDefinition const& _modifierDef) override;
|
||||
virtual void endVisit(ModifierDefinition const& _modifierDef) override;
|
||||
virtual void endVisit(Identifier const& _identifier) override;
|
||||
virtual bool visit(MemberAccess const& _memberAccess) override;
|
||||
virtual void endVisit(MemberAccess const& _memberAccess) override;
|
||||
virtual void endVisit(IndexAccess const& _indexAccess) override;
|
||||
virtual void endVisit(ModifierInvocation const& _modifier) override;
|
||||
|
@ -949,11 +949,25 @@ bool RationalNumberType::operator==(Type const& _other) const
|
||||
return m_value == other.m_value;
|
||||
}
|
||||
|
||||
string RationalNumberType::bigintToReadableString(dev::bigint const& _num)
|
||||
{
|
||||
string str = _num.str();
|
||||
if (str.size() > 32)
|
||||
{
|
||||
int omitted = str.size() - 8;
|
||||
str = str.substr(0, 4) + "...(" + to_string(omitted) + " digits omitted)..." + str.substr(str.size() - 4, 4);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
string RationalNumberType::toString(bool) const
|
||||
{
|
||||
if (!isFractional())
|
||||
return "int_const " + m_value.numerator().str();
|
||||
return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str();
|
||||
return "int_const " + bigintToReadableString(m_value.numerator());
|
||||
|
||||
string numerator = bigintToReadableString(m_value.numerator());
|
||||
string denominator = bigintToReadableString(m_value.denominator());
|
||||
return "rational_const " + numerator + " / " + denominator;
|
||||
}
|
||||
|
||||
u256 RationalNumberType::literalValue(Literal const*) const
|
||||
|
@ -257,7 +257,7 @@ public:
|
||||
}
|
||||
virtual u256 literalValue(Literal const*) const
|
||||
{
|
||||
solAssert(false, "Literal value requested for type without literals.");
|
||||
solAssert(false, "Literal value requested for type without literals: " + toString(false));
|
||||
}
|
||||
|
||||
/// @returns a (simpler) type that is encoded in the same way for external function calls.
|
||||
@ -437,6 +437,10 @@ private:
|
||||
|
||||
/// @returns true if the literal is a valid rational number.
|
||||
static std::tuple<bool, rational> parseRational(std::string const& _value);
|
||||
|
||||
/// @returns a truncated readable representation of the bigint keeping only
|
||||
/// up to 4 leading and 4 trailing digits.
|
||||
static std::string bigintToReadableString(dev::bigint const& num);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -120,7 +120,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
|
||||
Whiskers templ(R"(
|
||||
function <functionName>(headStart, dataEnd) -> <valueReturnParams> {
|
||||
switch slt(sub(dataEnd, headStart), <minimumSize>) case 1 { revert(0, 0) }
|
||||
if slt(sub(dataEnd, headStart), <minimumSize>) { revert(0, 0) }
|
||||
<decodeElements>
|
||||
}
|
||||
)");
|
||||
@ -151,7 +151,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
|
||||
R"(
|
||||
{
|
||||
let offset := <load>(add(headStart, <pos>))
|
||||
switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) }
|
||||
if gt(offset, 0xffffffffffffffff) { revert(0, 0) }
|
||||
<values> := <abiDecode>(add(headStart, offset), dataEnd)
|
||||
}
|
||||
)" :
|
||||
@ -1134,7 +1134,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
||||
R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> array {
|
||||
switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) }
|
||||
if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
|
||||
let length := <retrieveLength>
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
let dst := array
|
||||
@ -1169,7 +1169,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
|
||||
else
|
||||
{
|
||||
string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize());
|
||||
templ("staticBoundsCheck", "switch gt(add(src, mul(length, " + baseEncodedSize + ")), end) case 1 { revert(0, 0) }");
|
||||
templ("staticBoundsCheck", "if gt(add(src, mul(length, " + baseEncodedSize + ")), end) { revert(0, 0) }");
|
||||
templ("retrieveElementPos", "src");
|
||||
templ("baseEncodedSize", baseEncodedSize);
|
||||
}
|
||||
@ -1197,11 +1197,11 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
templ = R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> arrayPos, length {
|
||||
switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) }
|
||||
if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
|
||||
length := calldataload(offset)
|
||||
switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) }
|
||||
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
||||
arrayPos := add(offset, 0x20)
|
||||
switch gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) case 1 { revert(0, 0) }
|
||||
if gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) { revert(0, 0) }
|
||||
}
|
||||
)";
|
||||
else
|
||||
@ -1209,7 +1209,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
|
||||
// <readableTypeName>
|
||||
function <functionName>(offset, end) -> arrayPos {
|
||||
arrayPos := offset
|
||||
switch gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) case 1 { revert(0, 0) }
|
||||
if gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) { revert(0, 0) }
|
||||
}
|
||||
)";
|
||||
Whiskers w{templ};
|
||||
@ -1235,13 +1235,13 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _
|
||||
Whiskers templ(
|
||||
R"(
|
||||
function <functionName>(offset, end) -> array {
|
||||
switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) }
|
||||
if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
|
||||
let length := <load>(offset)
|
||||
array := <allocate>(<allocationSize>(length))
|
||||
mstore(array, length)
|
||||
let src := add(offset, 0x20)
|
||||
let dst := add(array, 0x20)
|
||||
switch gt(add(src, length), end) case 1 { revert(0, 0) }
|
||||
if gt(add(src, length), end) { revert(0, 0) }
|
||||
<copyToMemFun>(src, dst, length)
|
||||
}
|
||||
)"
|
||||
@ -1268,7 +1268,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
||||
Whiskers templ(R"(
|
||||
// <readableTypeName>
|
||||
function <functionName>(headStart, end) -> value {
|
||||
switch slt(sub(end, headStart), <minimumSize>) case 1 { revert(0, 0) }
|
||||
if slt(sub(end, headStart), <minimumSize>) { revert(0, 0) }
|
||||
value := <allocate>(<memorySize>)
|
||||
<#members>
|
||||
{
|
||||
@ -1296,7 +1296,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
||||
dynamic ?
|
||||
R"(
|
||||
let offset := <load>(add(headStart, <pos>))
|
||||
switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) }
|
||||
if gt(offset, 0xffffffffffffffff) { revert(0, 0) }
|
||||
mstore(add(value, <memoryOffset>), <abiDecode>(add(headStart, offset), end))
|
||||
)" :
|
||||
R"(
|
||||
@ -1501,7 +1501,7 @@ string ABIFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
|
||||
Whiskers w(R"(
|
||||
function <functionName>(length) -> size {
|
||||
// Make sure we can allocate memory without overflow
|
||||
switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) }
|
||||
if gt(length, 0xffffffffffffffff) { revert(0, 0) }
|
||||
size := <allocationSize>
|
||||
<addLengthSlot>
|
||||
}
|
||||
@ -1620,7 +1620,7 @@ string ABIFunctions::allocationFunction()
|
||||
memPtr := mload(<freeMemoryPointer>)
|
||||
let newFreePtr := add(memPtr, size)
|
||||
// protect against overflow
|
||||
switch or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) case 1 { revert(0, 0) }
|
||||
if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
|
||||
mstore(<freeMemoryPointer>, newFreePtr)
|
||||
}
|
||||
)")
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user