Merge pull request #8193 from ethereum/develop

Merge develop into release for 0.6.2.
This commit is contained in:
chriseth 2020-01-27 14:43:22 +01:00 committed by GitHub
commit bacdbe5787
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
707 changed files with 8762 additions and 10452 deletions

View File

@ -295,7 +295,6 @@ jobs:
environment:
CC: clang
CXX: clang++
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- run: *run_build
@ -305,8 +304,6 @@ jobs:
b_ubu: &build_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.docker-image-rev >>
environment:
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- run: *run_build
@ -317,13 +314,12 @@ jobs:
<<: *build_ubuntu1904
environment:
FORCE_RELEASE: ON
CMAKE_OPTIONS: -DLLL=ON
b_ubu18: &build_ubuntu1804
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1804-<< pipeline.parameters.docker-image-rev >>
environment:
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2 -DLLL=ON
CMAKE_OPTIONS: -DCMAKE_CXX_FLAGS=-O2
CMAKE_BUILD_TYPE: RelWithDebugInfo
steps:
- checkout
@ -368,7 +364,7 @@ jobs:
<<: *build_ubuntu1904
environment:
CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF -DLLL=ON
CMAKE_OPTIONS: -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/cxx20.cmake -DUSE_CVC4=OFF
steps:
- checkout
- run: *run_build
@ -408,7 +404,6 @@ jobs:
- image: archlinux/base
environment:
TERM: xterm
CMAKE_OPTIONS: -DLLL=ON
steps:
- run:
name: Install build dependencies
@ -425,7 +420,6 @@ jobs:
environment:
TERM: xterm
CMAKE_BUILD_TYPE: Debug
CMAKE_OPTIONS: -DLLL=ON
steps:
- checkout
- restore_cache:

View File

@ -10,7 +10,7 @@ include(EthPolicy)
eth_policy()
# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.6.1")
set(PROJECT_VERSION "0.6.2")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)
include(TestBigEndian)
@ -19,10 +19,7 @@ if (IS_BIG_ENDIAN)
message(FATAL_ERROR "${PROJECT_NAME} currently does not support big endian systems.")
endif()
option(LLL "Build LLL" OFF)
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" ${LLL})
# Setup cccache.
include(EthCcache)
@ -52,7 +49,7 @@ configure_file("${CMAKE_SOURCE_DIR}/cmake/templates/license.h.in" include/licens
include(EthOptions)
configure_project(TESTS)
add_subdirectory(libdevcore)
add_subdirectory(libsolutil)
add_subdirectory(liblangutil)
add_subdirectory(libevmasm)
add_subdirectory(libyul)
@ -61,10 +58,6 @@ add_subdirectory(libsolc)
if (NOT EMSCRIPTEN)
add_subdirectory(solc)
if (LLL)
add_subdirectory(liblll)
add_subdirectory(lllc)
endif()
endif()
if (TESTS AND NOT EMSCRIPTEN)

View File

@ -211,7 +211,7 @@ for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end();
## 12. Include Headers
1. Includes should go in increasing order of generality (`libsolidity` -> `libevmasm` -> `libdevcore` -> `boost` -> `STL`).
1. Includes should go in increasing order of generality (`libsolidity` -> `libevmasm` -> `libsolutil` -> `boost` -> `STL`).
2. The corresponding `.h` file should be the first include in the respective `.cpp` file.
3. Insert empty lines between blocks of include files.
@ -226,8 +226,8 @@ Example:
#include <libevmasm/GasMeter.h>
#include <libdevcore/Common.h>
#include <libdevcore/SHA3.h>
#include <libsolutil/Common.h>
#include <libsolutil/SHA3.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/algorithm/string/replace.hpp>

View File

@ -1,3 +1,29 @@
### 0.6.2 (2020-01-27)
Language Features:
* Allow accessing external functions via contract and interface names to obtain their selector.
* Allow interfaces to inherit from other interfaces
* Allow gas and value to be set in external function calls using ``c.f{gas: 10000, value: 4 ether}()``.
* Allow specifying the ``salt`` for contract creations and thus the ``create2`` opcode using ``new C{salt: 0x1234, value: 1 ether}(arg1, arg2)``.
* Inline Assembly: Support literals ``true`` and ``false``.
Compiler Features:
* LLL: The LLL compiler has been removed.
* General: Raise warning if runtime bytecode exceeds 24576 bytes (a limit introduced in Spurious Dragon).
* General: Support compiling starting from an imported AST. Among others, this can be used for mutation testing.
* Yul Optimizer: Apply penalty when trying to rematerialize into loops.
Bugfixes:
* Commandline interface: Only activate yul optimizer if ``--optimize`` is given.
* Fixes internal compiler error on explicitly calling unimplemented base functions.
Build System:
* Switch to building soljson.js with an embedded base64-encoded wasm binary.
### 0.6.1 (2020-01-02)
Bugfixes:
@ -900,7 +926,7 @@ Features:
* ABI JSON: Include new field ``stateMutability`` with values ``pure``, ``view``,
``nonpayable`` and ``payable``.
* Analyzer: Experimental partial support for Z3 SMT checker ("SMTChecker").
* Build System: Shared libraries (``libdevcore``, ``libevmasm``, ``libsolidity``
* Build System: Shared libraries (``libsolutil``, ``libevmasm``, ``libsolidity``
and ``liblll``) are no longer produced during the build process.
* Code generator: Experimental new implementation of ABI encoder that can
encode arbitrarily nested arrays ("ABIEncoderV2")

View File

@ -34,6 +34,9 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-Werror)
add_compile_options(-pedantic)
add_compile_options(-Wno-unknown-pragmas)
add_compile_options(-Wimplicit-fallthrough)
# Configuration-specific compiler settings.
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -DETH_DEBUG")
@ -110,9 +113,13 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s STRICT=1")
# Export the Emscripten-generated auxiliary methods which are needed by solc-js.
# Which methods of libsolc itself are exported is specified in libsolc/CMakeLists.txt.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','_malloc','stringToUTF8','setValue']")
# Do not build as a WebAssembly target - we need an asm.js output.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=0")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s EXTRA_EXPORTED_RUNTIME_METHODS=['cwrap','addFunction','removeFunction','UTF8ToString','lengthBytesUTF8','stringToUTF8','setValue']")
# Build for webassembly target.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM=1")
# Set webassembly build to synchronous loading.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WASM_ASYNC_COMPILATION=0")
# Output a single js file with the wasm binary embedded as base64 string.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s SINGLE_FILE=1")
# Disable warnings about not being pure asm.js due to memory growth.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-almost-asm")

View File

@ -41,7 +41,6 @@ if (SUPPORT_TOOLS)
endif()
message("------------------------------------------------------------------ flags")
message("-- OSSFUZZ ${OSSFUZZ}")
message("-- LLL ${LLL}")
message("------------------------------------------------------------------------")
message("")
endmacro()

View File

@ -1,41 +1,20 @@
#################
Solidity Assembly
#################
.. _inline-assembly:
###############
Inline Assembly
###############
.. index:: ! assembly, ! asm, ! evmasm
Solidity defines an assembly language that you can use without Solidity and also
as "inline assembly" inside Solidity source code. This guide starts with describing
how to use inline assembly, how it differs from standalone assembly
(sometimes also referred to by its proper name "Yul"), and
specifies assembly itself.
.. _inline-assembly:
Inline Assembly
===============
You can interleave Solidity statements with inline assembly in a language close
to the one of the virtual machine. This gives you more fine-grained control,
especially when you are enhancing the language by writing libraries.
to the one of the Ethereum virtual machine. This gives you more fine-grained control,
which is especially useful when you are enhancing the language by writing libraries.
As the EVM is a stack machine, it is often hard to address the correct stack slot
and provide arguments to opcodes at the correct point on the stack. Solidity's inline
assembly helps you do this, and with other issues that arise when writing manual assembly.
The language used for inline assembly in Solidity is called `Yul <yul>`_
and it is documented in its own section. This section will only cover
how the inline assembly code can interface with the surrounding Solidity code.
For inline assembly, the stack is actually not visible at all, but if you look
closer, there is always a very direct translation from inline assembly to
the stack based EVM opcode stream.
Inline assembly has the following features:
* functional-style opcodes: ``mul(1, add(2, 3))``
* assembly-local variables: ``let x := add(2, 3) let y := mload(0x40) x := add(x, y)``
* access to external variables: ``function f(uint x) public { assembly { x := sub(x, 1) } }``
* 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) }``
* switch statements: ``switch x case 0 { y := mul(x, 2) } default { y := 0 }``
* function calls: ``function f(x) -> y { switch x case 0 { y := 1 } default { y := mul(x, f(sub(x, 1))) } }``
.. warning::
Inline assembly is a way to access the Ethereum Virtual Machine
@ -43,24 +22,14 @@ Inline assembly has the following features:
features and checks of Solidity. You should only use it for
tasks that need it, and only if you are confident with using it.
Syntax
------
Assembly parses comments, literals and identifiers in the same way as Solidity, so you can use the
usual ``//`` and ``/* */`` comments. There is one exception: Identifiers in inline assembly can contain
``.``. Inline assembly is marked by ``assembly { ... }`` and inside
these curly braces, you can use the following (see the later sections for more details):
An inline assembly block is marked by ``assembly { ... }``, where the code inside
the curly braces is code in the `Yul <yul>`_ language.
- literals, i.e. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
- opcodes in functional style, e.g. ``add(1, mload(0))``
- variable declarations, e.g. ``let x := 7``, ``let x := add(y, 3)`` or ``let x`` (initial value of 0 is assigned)
- identifiers (assembly-local variables and externals if used as inline assembly), e.g. ``add(3, x)``, ``sstore(x_slot, 2)``
- assignments, e.g. ``x := add(y, 3)``
- blocks where local variables are scoped inside, e.g. ``{ let x := 3 { let y := add(x, 1) } }``
The inline assembly code can access local Solidity variables as explained below.
Inline assembly manages local variables and control-flow. Because of that,
opcodes that interfere with these features are not available. This includes
the ``dup`` and ``swap`` instructions as well as ``jump`` instructions and labels.
Different inline assembly blocks share no namespace, i.e. it is not possible
to call a Yul function or access a Yul variable defined in a different inline assembly block.
Example
-------
@ -146,238 +115,20 @@ efficient code, for example:
}
.. _opcodes:
Opcodes
-------
This document does not want to be a full description of the Ethereum virtual machine, but the
following list can be used as a quick reference of its opcodes.
If an opcode takes arguments, they are given in parentheses.
Opcodes marked with ``-`` do not return a result,
those marked with ``*`` are special in a certain way and all others return exactly one value.
Opcodes marked with ``F``, ``H``, ``B``, ``C`` or ``I`` are present since Frontier, Homestead,
Byzantium, Constantinople or Istanbul, respectively.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
but not including position ``b`` and ``storage[p]`` signifies the storage contents at slot ``p``.
In the grammar, opcodes are represented as pre-defined identifiers ("built-in functions").
+-------------------------+-----+---+-----------------------------------------------------------------+
| Instruction | | | Explanation |
+=========================+=====+===+=================================================================+
| stop() + `-` | F | stop execution, identical to return(0, 0) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| add(x, y) | | F | x + y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| sub(x, y) | | F | x - y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| mul(x, y) | | F | x * y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| div(x, y) | | F | x / y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| sdiv(x, y) | | F | x / y, for signed numbers in two's complement |
+-------------------------+-----+---+-----------------------------------------------------------------+
| mod(x, y) | | F | x % y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| smod(x, y) | | F | x % y, for signed numbers in two's complement |
+-------------------------+-----+---+-----------------------------------------------------------------+
| exp(x, y) | | F | x to the power of y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| not(x) | | F | ~x, every bit of x is negated |
+-------------------------+-----+---+-----------------------------------------------------------------+
| lt(x, y) | | F | 1 if x < y, 0 otherwise |
+-------------------------+-----+---+-----------------------------------------------------------------+
| gt(x, y) | | F | 1 if x > y, 0 otherwise |
+-------------------------+-----+---+-----------------------------------------------------------------+
| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
+-------------------------+-----+---+-----------------------------------------------------------------+
| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
+-------------------------+-----+---+-----------------------------------------------------------------+
| eq(x, y) | | F | 1 if x == y, 0 otherwise |
+-------------------------+-----+---+-----------------------------------------------------------------+
| iszero(x) | | F | 1 if x == 0, 0 otherwise |
+-------------------------+-----+---+-----------------------------------------------------------------+
| and(x, y) | | F | bitwise "and" of x and y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| or(x, y) | | F | bitwise "or" of x and y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| xor(x, y) | | F | bitwise "xor" of x and y |
+-------------------------+-----+---+-----------------------------------------------------------------+
| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte |
+-------------------------+-----+---+-----------------------------------------------------------------+
| shl(x, y) | | C | logical shift left y by x bits |
+-------------------------+-----+---+-----------------------------------------------------------------+
| shr(x, y) | | C | logical shift right y by x bits |
+-------------------------+-----+---+-----------------------------------------------------------------+
| sar(x, y) | | C | signed arithmetic shift right y by x bits |
+-------------------------+-----+---+-----------------------------------------------------------------+
| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetic |
+-------------------------+-----+---+-----------------------------------------------------------------+
| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetic |
+-------------------------+-----+---+-----------------------------------------------------------------+
| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant |
+-------------------------+-----+---+-----------------------------------------------------------------+
| keccak256(p, n) | | F | keccak(mem[p...(p+n))) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| pc() | | F | current position in code |
+-------------------------+-----+---+-----------------------------------------------------------------+
| pop(x) | `-` | F | discard value x |
+-------------------------+-----+---+-----------------------------------------------------------------+
| mload(p) | | F | mem[p...(p+32)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| mstore(p, v) | `-` | F | mem[p...(p+32)) := v |
+-------------------------+-----+---+-----------------------------------------------------------------+
| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| sload(p) | | F | storage[p] |
+-------------------------+-----+---+-----------------------------------------------------------------+
| sstore(p, v) | `-` | F | storage[p] := v |
+-------------------------+-----+---+-----------------------------------------------------------------+
| msize() | | F | size of memory, i.e. largest accessed memory index |
+-------------------------+-----+---+-----------------------------------------------------------------+
| gas() | | F | gas still available to execution |
+-------------------------+-----+---+-----------------------------------------------------------------+
| address() | | F | address of the current contract / execution context |
+-------------------------+-----+---+-----------------------------------------------------------------+
| balance(a) | | F | wei balance at address a |
+-------------------------+-----+---+-----------------------------------------------------------------+
| selfbalance() | | I | equivalent to balance(address()), but cheaper |
+-------------------------+-----+---+-----------------------------------------------------------------+
| caller() | | F | call sender (excluding ``delegatecall``) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| callvalue() | | F | wei sent together with the current call |
+-------------------------+-----+---+-----------------------------------------------------------------+
| calldataload(p) | | F | call data starting from position p (32 bytes) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| calldatasize() | | F | size of call data in bytes |
+-------------------------+-----+---+-----------------------------------------------------------------+
| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t |
+-------------------------+-----+---+-----------------------------------------------------------------+
| codesize() | | F | size of the code of the current contract / execution context |
+-------------------------+-----+---+-----------------------------------------------------------------+
| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t |
+-------------------------+-----+---+-----------------------------------------------------------------+
| extcodesize(a) | | F | size of the code at address a |
+-------------------------+-----+---+-----------------------------------------------------------------+
| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a |
+-------------------------+-----+---+-----------------------------------------------------------------+
| returndatasize() | | B | size of the last returndata |
+-------------------------+-----+---+-----------------------------------------------------------------+
| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t |
+-------------------------+-----+---+-----------------------------------------------------------------+
| extcodehash(a) | | C | code hash of address a |
+-------------------------+-----+---+-----------------------------------------------------------------+
| create(v, p, n) | | F | create new contract with code mem[p...(p+n)) and send v wei |
| | | | and return the new address |
+-------------------------+-----+---+-----------------------------------------------------------------+
| create2(v, p, n, s) | | C | create new contract with code mem[p...(p+n)) at address |
| | | | keccak256(0xff . this . s . keccak256(mem[p...(p+n))) |
| | | | and send v wei and return the new address, where ``0xff`` is a |
| | | | 1 byte value, ``this`` is the current contract's address |
| | | | as a 20 byte value and ``s`` is a big-endian 256-bit value |
+-------------------------+-----+---+-----------------------------------------------------------------+
| call(g, a, v, in, | | F | call contract at address a with input mem[in...(in+insize)) |
| insize, out, outsize) | | | providing g gas and v wei and output area |
| | | | mem[out...(out+outsize)) returning 0 on error (eg. out of gas) |
| | | | and 1 on success |
+-------------------------+-----+---+-----------------------------------------------------------------+
| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay |
| insize, out, outsize) | | | in the context of the current contract otherwise |
+-------------------------+-----+---+-----------------------------------------------------------------+
| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` |
| insize, out, outsize) | | | and ``callvalue`` |
+-------------------------+-----+---+-----------------------------------------------------------------+
| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
| insize, out, outsize) | | | not allow state modifications |
+-------------------------+-----+---+-----------------------------------------------------------------+
| return(p, s) | `-` | F | end execution, return data mem[p...(p+s)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p...(p+s)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a |
+-------------------------+-----+---+-----------------------------------------------------------------+
| invalid() | `-` | F | end execution with invalid instruction |
+-------------------------+-----+---+-----------------------------------------------------------------+
| log0(p, s) | `-` | F | log without topics and data mem[p...(p+s)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p...(p+s)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p...(p+s)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p...(p+s)) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p...(p+s)) |
| t4) | | | |
+-------------------------+-----+---+-----------------------------------------------------------------+
| chainid() | | I | ID of the executing chain (EIP 1344) |
+-------------------------+-----+---+-----------------------------------------------------------------+
| origin() | | F | transaction sender |
+-------------------------+-----+---+-----------------------------------------------------------------+
| gasprice() | | F | gas price of the transaction |
+-------------------------+-----+---+-----------------------------------------------------------------+
| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current |
+-------------------------+-----+---+-----------------------------------------------------------------+
| coinbase() | | F | current mining beneficiary |
+-------------------------+-----+---+-----------------------------------------------------------------+
| timestamp() | | F | timestamp of the current block in seconds since the epoch |
+-------------------------+-----+---+-----------------------------------------------------------------+
| number() | | F | current block number |
+-------------------------+-----+---+-----------------------------------------------------------------+
| difficulty() | | F | difficulty of the current block |
+-------------------------+-----+---+-----------------------------------------------------------------+
| gaslimit() | | F | block gas limit of the current block |
+-------------------------+-----+---+-----------------------------------------------------------------+
Literals
--------
You can use integer constants by typing them in decimal or hexadecimal notation and an
appropriate ``PUSHi`` instruction will automatically be generated. The following creates code
to add 2 and 3 resulting in 5 and then computes the bitwise ``AND`` with the string "abc".
The final value is assigned to a local variable called ``x``.
Strings are stored left-aligned and cannot be longer than 32 bytes.
.. code::
assembly { let x := and("abc", add(3, 2)) }
Functional Style
-----------------
For a sequence of opcodes, it is often hard to see what the actual
arguments for certain opcodes are. In the following example,
``3`` is added to the contents in memory at position ``0x80``.
.. code::
3 0x80 mload add 0x80 mstore
Solidity inline assembly has a "functional style" notation where the same code
would be written as follows:
.. code::
mstore(0x80, add(mload(0x80), 3))
If you read the code from right to left, you end up with exactly the same
sequence of constants and opcodes, but it is much clearer where the
values end up.
If you care about the exact stack layout, just note that the
syntactically first argument for a function or opcode will be put at the
top of the stack.
Access to External Variables, Functions and Libraries
-----------------------------------------------------
You can access Solidity variables and other identifiers by using their name.
For variables stored in the memory data location, this pushes the address, and not the value
onto the stack. Variables stored in the storage data location are different, as they might not
occupy a full storage slot, so their "address" is composed of a slot and a byte-offset
Local variables of value type are directly usable in inline assembly.
Local variables that refer to memory or calldata evaluate to the
address of the variable in memory, resp. calldata, not the value itself.
For local storage variables or state variables, a single Yul identifier
is not sufficient, since they do not necessarily occupy a single full storage slot.
Therefore, their "address" is composed of a slot and a byte-offset
inside that slot. To retrieve the slot pointed to by the variable ``x``, you
use ``x_slot``, and to retrieve the byte-offset you use ``x_offset``.
@ -391,7 +142,9 @@ Local Solidity variables are available for assignments, for example:
uint b;
function f(uint x) public view returns (uint r) {
assembly {
r := mul(x, sload(b_slot)) // ignore the offset, we know it is zero
// We ignore the storage slot offset, we know it is zero
// in this special case.
r := mul(x, sload(b_slot))
}
}
}
@ -407,177 +160,19 @@ Local Solidity variables are available for assignments, for example:
To clean signed types, you can use the ``signextend`` opcode:
``assembly { signextend(<num_bytes_of_x_minus_one>, x) }``
Declaring Assembly-Local Variables
----------------------------------
You can use the ``let`` keyword to declare variables that are only visible in
inline assembly and actually only in the current ``{...}``-block. What happens
is that the ``let`` instruction will create a new stack slot that is reserved
for the variable and automatically removed again when the end of the block
is reached. You need to provide an initial value for the variable which can
be just ``0``, but it can also be a complex functional-style expression.
Since 0.6.0 the name of a declared variable may not end in ``_offset`` or ``_slot``
Since Solidity 0.6.0 the name of a inline assembly variable may not end in ``_offset`` or ``_slot``
and it may not shadow any declaration visible in the scope of the inline assembly block
(including variable, contract and function declarations). Similarly, if the name of a declared
variable contains a dot ``.``, the prefix up to the ``.`` may not conflict with any
declaration visible in the scope of the inline assembly block.
.. code::
pragma solidity >=0.4.16 <0.7.0;
contract C {
function f(uint x) public view returns (uint b) {
assembly {
let v := add(x, 1)
mstore(0x80, v)
{
let y := add(sload(v), 1)
b := y
} // y is "deallocated" here
b := add(b, v)
} // v is "deallocated" here
}
}
Assignments
-----------
Assignments are possible to assembly-local variables and to function-local
variables. Take care that when you assign to variables that point to
memory or storage, you will only change the pointer and not the data.
Variables can only be assigned expressions that result in exactly one value.
If you want to assign the values returned from a function that has
multiple return parameters, you have to provide multiple variables.
.. code::
{
let v := 0
let g := add(v, 2)
function f() -> a, b { }
let c, d := f()
}
If
--
The if statement can be used for conditionally executing code.
There is no "else" part, consider using "switch" (see below) if
you need multiple alternatives.
.. code::
{
if eq(value, 0) { revert(0, 0) }
}
The curly braces for the body are required.
Switch
------
You can use a switch statement as a very basic version of "if/else".
It takes the value of an expression and compares it to several constants.
The branch corresponding to the matching constant is taken. Contrary to the
error-prone behaviour of some programming languages, control flow does
not continue from one case to the next. There can be a fallback or default
case called ``default``.
.. code::
{
let x := 0
switch calldataload(4)
case 0 {
x := calldataload(0x24)
}
default {
x := calldataload(0x44)
}
sstore(0, div(x, 2))
}
The list of cases does not require curly braces, but the body of a
case does require them.
Loops
-----
Assembly supports a simple for-style loop. For-style loops have
a header containing an initializing part, a condition and a post-iteration
part. The condition has to be a functional-style expression, while
the other two are blocks. If the initializing part
declares any variables, the scope of these variables is extended into the
body (including the condition and the post-iteration part).
The ``break`` and ``continue`` statements can be used to exit the loop
or skip to the post-part, respectively.
The following example computes the sum of an area in memory.
.. code::
{
let x := 0
for { let i := 0 } lt(i, 0x100) { i := add(i, 0x20) } {
x := add(x, mload(i))
}
}
For loops can also be written so that they behave like while loops:
Simply leave the initialization and post-iteration parts empty.
.. code::
{
let x := 0
let i := 0
for { } lt(i, 0x100) { } { // while(i < 0x100)
x := add(x, mload(i))
i := add(i, 0x20)
}
}
Functions
---------
Assembly allows the definition of low-level functions. These take their
arguments (and a return PC) from the stack and also put the results onto the
stack. Calling a function looks the same way as executing a functional-style
opcode.
Functions can be defined anywhere and are visible in the block they are
declared in. Inside a function, you cannot access local variables
defined outside of that function.
If you call a function that returns multiple values, you have to assign
them to a tuple using ``a, b := f(x)`` or ``let a, b := f(x)``.
The ``leave`` statement can be used to exit the current function. It
works like the ``return`` statement in other languages just that it does
not take a value to return, it just exits the functions and the function
will return whatever values are currently assigned to the return variable(s).
The following example implements the power function by square-and-multiply.
.. code::
{
function power(base, exponent) -> result {
switch exponent
case 0 { result := 1 }
case 1 { result := base }
default {
result := power(mul(base, base), div(exponent, 2))
switch mod(exponent, 2)
case 1 { result := mul(base, result) }
}
}
}
Things to Avoid
---------------
@ -593,7 +188,8 @@ Conventions in Solidity
-----------------------
In contrast to EVM assembly, Solidity has types which are narrower than 256 bits,
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that types can be shorter than 256
e.g. ``uint24``. For efficiency, most arithmetic operations ignore the fact that
types can be shorter than 256
bits, and the higher-order bits are cleaned when necessary,
i.e., shortly before they are written to memory or before comparisons are performed.
This means that if you access such a variable
@ -630,157 +226,3 @@ first slot of the array and followed by the array elements.
to allow better convertibility between statically- and dynamically-sized arrays, so
do not rely on this.
Standalone Assembly
===================
The assembly language described as inline assembly above can also be used
standalone and in fact, the plan is to use it as an intermediate language
for the Solidity compiler. In this form, it tries to achieve several goals:
1. Programs written in it should be readable, even if the code is generated by a compiler from Solidity.
2. The translation from assembly to bytecode should contain as few "surprises" as possible.
3. Control flow should be easy to detect to help in formal verification and optimization.
In order to achieve the first and last goal, assembly provides high-level constructs
like ``for`` loops, ``if`` and ``switch`` statements and function calls. It should be possible
to write assembly programs that do not make use of explicit ``SWAP``, ``DUP``,
``JUMP`` and ``JUMPI`` statements, because the first two obfuscate the data flow
and the last two obfuscate control flow. Furthermore, functional statements of
the form ``mul(add(x, y), 7)`` are preferred over pure opcode statements like
``7 y x add mul`` because in the first form, it is much easier to see which
operand is used for which opcode.
The second goal is achieved by compiling the
higher level constructs to bytecode in a very regular way.
The only non-local operation performed
by the assembler is name lookup of user-defined identifiers (functions, variables, ...),
which follow very simple and regular scoping rules and cleanup of local variables from the stack.
Scoping: An identifier that is declared (label, variable, function, assembly)
is only visible in the block where it was declared (including nested blocks
inside the current block). It is not legal to access local variables across
function borders, even if they would be in scope. Shadowing is not allowed.
Local variables cannot be accessed before they were declared, but
functions and assemblies can. Assemblies are special blocks that are used
for e.g. returning runtime code or creating contracts. No identifier from an
outer assembly is visible in a sub-assembly.
If control flow passes over the end of a block, pop instructions are inserted
that match the number of local variables declared in that block.
Whenever a local variable is referenced, the code generator needs
to know its current relative position in the stack and thus it needs to
keep track of the current so-called stack height. Since all local variables
are removed at the end of a block, the stack height before and after the block
should be the same. If this is not the case, compilation fails.
Using ``switch``, ``for`` and functions, it should be possible to write
complex code without using ``jump`` or ``jumpi`` manually. This makes it much
easier to analyze the control flow, which allows for improved formal
verification and optimization.
Furthermore, if manual jumps are allowed, computing the stack height is rather complicated.
The position of all local variables on the stack needs to be known, otherwise
neither references to local variables nor removing local variables automatically
from the stack at the end of a block will work properly.
Example:
We will follow an example compilation from Solidity to assembly.
We consider the runtime bytecode of the following Solidity program::
pragma solidity >=0.4.16 <0.7.0;
contract C {
function f(uint x) public pure returns (uint y) {
y = 1;
for (uint i = 0; i < x; i++)
y = 2 * y;
}
}
The following assembly will be generated::
{
mstore(0x40, 0x80) // store the "free memory pointer"
// function dispatcher
switch div(calldataload(0), exp(2, 226))
case 0xb3de648b {
let r := f(calldataload(4))
let ret := $allocate(0x20)
mstore(ret, r)
return(ret, 0x20)
}
default { revert(0, 0) }
// memory allocator
function $allocate(size) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, size))
}
// the contract function
function f(x) -> y {
y := 1
for { let i := 0 } lt(i, x) { i := add(i, 1) } {
y := mul(2, y)
}
}
}
Assembly Grammar
----------------
The tasks of the parser are the following:
- Turn the byte stream into a token stream, discarding C++-style comments
(a special comment exists for source references, but we will not explain it here).
- Turn the token stream into an AST according to the grammar below
- Register identifiers with the block they are defined in (annotation to the
AST node) and note from which point on, variables can be accessed.
The assembly lexer follows the one defined by Solidity itself.
Whitespace is used to delimit tokens and it consists of the characters
Space, Tab and Linefeed. Comments are regular JavaScript/C++ comments and
are interpreted in the same way as Whitespace.
Grammar::
AssemblyBlock = '{' AssemblyItem* '}'
AssemblyItem =
Identifier |
AssemblyBlock |
AssemblyExpression |
AssemblyLocalDefinition |
AssemblyAssignment |
AssemblyIf |
AssemblySwitch |
AssemblyFunctionDefinition |
AssemblyFor |
'break' |
'continue' |
'leave' |
SubAssembly
AssemblyExpression = AssemblyCall | Identifier | AssemblyLiteral
AssemblyLiteral = NumberLiteral | StringLiteral | HexLiteral
Identifier = [a-zA-Z_$] [a-zA-Z_0-9.]*
AssemblyCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')'
AssemblyLocalDefinition = 'let' IdentifierOrList ( ':=' AssemblyExpression )?
AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression
IdentifierOrList = Identifier | '(' IdentifierList ')'
IdentifierList = Identifier ( ',' Identifier)*
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
AssemblySwitch = 'switch' AssemblyExpression AssemblyCase*
( 'default' AssemblyBlock )?
AssemblyCase = 'case' AssemblyExpression AssemblyBlock
AssemblyFunctionDefinition = 'function' Identifier '(' IdentifierList? ')'
( '->' '(' IdentifierList ')' )? AssemblyBlock
AssemblyFor = 'for' ( AssemblyBlock | AssemblyExpression )
AssemblyExpression ( AssemblyBlock | AssemblyExpression ) AssemblyBlock
SubAssembly = 'assembly' Identifier AssemblyBlock
NumberLiteral = HexNumber | DecimalNumber
HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
HexNumber = '0x' [0-9a-fA-F]+
DecimalNumber = [0-9]+

View File

@ -880,5 +880,9 @@
"0.6.1": {
"bugs": [],
"released": "2020-01-02"
},
"0.6.2": {
"bugs": [],
"released": "2020-01-27"
}
}

View File

@ -39,12 +39,12 @@ if they are marked ``virtual``. For details, please see
}
}
contract mortal is owned {
contract destructible 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
// `owned` and applies it to the `destroy` function, which
// causes that calls to `destroy` only have an effect if
// they are made by the stored owner.
function close() public onlyOwner {
function destroy() public onlyOwner {
selfdestruct(owner);
}
}
@ -58,7 +58,7 @@ if they are marked ``virtual``. For details, please see
}
}
contract Register is priced, owned {
contract Register is priced, destructible {
mapping (address => bool) registeredAddresses;
uint price;

View File

@ -335,7 +335,7 @@ operations as long as there is enough gas passed on to it.
::
pragma solidity ^0.6.0;
pragma solidity >0.6.1 <0.7.0;
contract Test {
// This function is called for all messages sent to
@ -382,7 +382,7 @@ operations as long as there is enough gas passed on to it.
(bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
require(success);
// results in test.x becoming == 1 and test.y becoming 0.
(success,) = address(test).call.value(1)(abi.encodeWithSignature("nonExistingFunction()"));
(success,) = address(test).call{value: 1}(abi.encodeWithSignature("nonExistingFunction()"));
require(success);
// results in test.x becoming == 1 and test.y becoming 1.

View File

@ -51,10 +51,10 @@ Details are given in the following example.
// contracts can access all non-private members including
// internal functions and state variables. These cannot be
// accessed externally via `this`, though.
contract Mortal is Owned {
contract Destructible is Owned {
// The keyword `virtual` means that the function can change
// its behaviour in derived classes ("overriding").
function kill() virtual public {
function destroy() virtual public {
if (msg.sender == owner) selfdestruct(owner);
}
}
@ -76,9 +76,9 @@ Details are given in the following example.
// Multiple inheritance is possible. Note that `owned` is
// also a base class of `mortal`, yet there is only a single
// also a base class of `Destructible`, yet there is only a single
// instance of `owned` (as for virtual inheritance in C++).
contract Named is Owned, Mortal {
contract Named is Owned, Destructible {
constructor(bytes32 name) public {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name);
@ -92,13 +92,13 @@ Details are given in the following example.
// If you want the function to override, you need to use the
// `override` keyword. You need to specify the `virtual` keyword again
// if you want this function to be overridden again.
function kill() public virtual override {
function destroy() public virtual override {
if (msg.sender == owner) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).unregister();
// It is still possible to call a specific
// overridden function.
Mortal.kill();
Destructible.destroy();
}
}
}
@ -107,21 +107,21 @@ Details are given in the following example.
// If a constructor takes an argument, it needs to be
// provided in the header (or modifier-invocation-style at
// the constructor of the derived contract (see below)).
contract PriceFeed is Owned, Mortal, Named("GoldFeed") {
contract PriceFeed is Owned, Destructible, Named("GoldFeed") {
function updateInfo(uint newInfo) public {
if (msg.sender == owner) info = newInfo;
}
// Here, we only specify `override` and not `virtual`.
// This means that contracts deriving from `PriceFeed`
// cannot change the behaviour of `kill` anymore.
function kill() public override(Mortal, Named) { Named.kill(); }
// cannot change the behaviour of `destroy` anymore.
function destroy() public override(Destructible, Named) { Named.destroy(); }
function get() public view returns(uint r) { return info; }
uint info;
}
Note that above, we call ``mortal.kill()`` to "forward" the
Note that above, we call ``Destructible.destroy()`` to "forward" the
destruction request. The way this is done is problematic, as
seen in the following example::
@ -132,27 +132,27 @@ seen in the following example::
address payable owner;
}
contract mortal is owned {
function kill() public virtual {
contract Destructible is owned {
function destroy() public virtual {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() public virtual override { /* do cleanup 1 */ mortal.kill(); }
contract Base1 is Destructible {
function destroy() public virtual override { /* do cleanup 1 */ Destructible.destroy(); }
}
contract Base2 is mortal {
function kill() public virtual override { /* do cleanup 2 */ mortal.kill(); }
contract Base2 is Destructible {
function destroy() public virtual override { /* do cleanup 2 */ Destructible.destroy(); }
}
contract Final is Base1, Base2 {
function kill() public override(Base1, Base2) { Base2.kill(); }
function destroy() public override(Base1, Base2) { Base2.destroy(); }
}
A call to ``Final.kill()`` will call ``Base2.kill`` because we specify it
A call to ``Final.destroy()`` will call ``Base2.destroy`` because we specify it
explicitly in the final override, but this function will bypass
``Base1.kill``. The way around this is to use ``super``::
``Base1.destroy``. The way around this is to use ``super``::
pragma solidity >=0.4.22 <0.7.0;
@ -161,31 +161,31 @@ explicitly in the final override, but this function will bypass
address payable owner;
}
contract mortal is owned {
function kill() virtual public {
contract Destructible is owned {
function destroy() virtual public {
if (msg.sender == owner) selfdestruct(owner);
}
}
contract Base1 is mortal {
function kill() public virtual override { /* do cleanup 1 */ super.kill(); }
contract Base1 is Destructible {
function destroy() public virtual override { /* do cleanup 1 */ super.destroy(); }
}
contract Base2 is mortal {
function kill() public virtual override { /* do cleanup 2 */ super.kill(); }
contract Base2 is Destructible {
function destroy() public virtual override { /* do cleanup 2 */ super.destroy(); }
}
contract Final is Base1, Base2 {
function kill() public override(Base1, Base2) { super.kill(); }
function destroy() public override(Base1, Base2) { super.destroy(); }
}
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 ``Base1.kill()`` (note that
inheritance graph, so it will call ``Base1.destroy()`` (note that
the final inheritance sequence is -- starting with the most
derived contract: Final, Base2, Base1, mortal, owned).
derived contract: Final, Base2, Base1, Destructible, 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

View File

@ -8,7 +8,7 @@ Interfaces
Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions:
- They cannot inherit other contracts or interfaces.
- They cannot inherit from other contracts, but they can inherit from other interfaces.
- All declared functions must be external.
- They cannot declare a constructor.
- They cannot declare state variables.
@ -37,10 +37,31 @@ they can be overridden. This does not automatically mean that an overriding func
can be overridden again - this is only possible if the overriding
function is marked ``virtual``.
Interfaces can inherit from other interfaces. This has the same rules as normal
inheritance.
::
pragma solidity >0.6.1 <0.7.0;
interface ParentA {
function test() external returns (uint256);
}
interface ParentB {
function test() external returns (uint256);
}
interface SubInterface is ParentA, ParentB {
// Must redefine test in order to assert that the parent
// meanings are compatible.
function test() external override(ParentA, ParentB) returns (uint256);
}
Types defined inside interfaces and other contract-like structures
can be accessed from other contracts: ``Token.TokenType`` or ``Token.Coin``.
.. warning:
Interfaces have supported ``enum`` types since :doc:`Solidity version 0.5.0 <050-breaking-changes>`, make
sure the pragma version specifies this version as a minimum.
sure the pragma version specifies this version as a minimum.

View File

@ -328,7 +328,7 @@ Whiskers
compiler in various places to aid readability, and thus maintainability and verifiability, of the code.
The syntax comes with a substantial difference to Mustache. The template markers ``{{`` and ``}}`` are
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`inline-assembly`
replaced by ``<`` and ``>`` in order to aid parsing and avoid conflicts with :ref:`yul`
(The symbols ``<`` and ``>`` are invalid in inline assembly, while ``{`` and ``}`` are used to delimit blocks).
Another limitation is that lists are only resolved one depth and they do not recurse. This may change in the future.

View File

@ -75,9 +75,10 @@ all function arguments have to be copied to memory.
it is a message call as part of the overall transaction.
When calling functions of other contracts, you can specify the amount of Wei or
gas sent with the call with the special options ``.value()`` and ``.gas()``,
respectively. Any Wei you send to the contract is added to the total balance
of the contract:
gas sent with the call with the special options ``{value: 10, gas: 10000}``.
Note that it is discouraged to specify gas values explicitly, since the gas costs
of opcodes can change in the future. Any Wei you send to the contract is added
to the total balance of that contract:
::
@ -90,14 +91,14 @@ of the contract:
contract Consumer {
InfoFeed feed;
function setFeed(InfoFeed addr) public { feed = addr; }
function callFeed() public { feed.info.value(10).gas(800)(); }
function callFeed() public { feed.info{value: 10, gas: 800}(); }
}
You need to use the modifier ``payable`` with the ``info`` function because
otherwise, the ``.value()`` option would not be available.
otherwise, the ``value`` option would not be available.
.. warning::
Be careful that ``feed.info.value(10).gas(800)`` only locally sets the
Be careful that ``feed.info{value: 10, gas: 800}`` only locally sets the
``value`` and amount of ``gas`` sent with the function call, and the
parentheses at the end perform the actual call. So in this case, the
function is not called and the ``value`` and ``gas`` settings are lost.
@ -121,6 +122,11 @@ throws an exception or goes out of gas.
external functions happen after any changes to state variables in your contract
so your contract is not vulnerable to a reentrancy exploit.
.. note::
Before Solidity 0.6.2, the recommended way to specify the value and gas
was to use ``f.value(x).gas(g)()``. This is still possible but deprecated
and will be removed with Solidity 0.7.0.
Named Calls and Anonymous Function Parameters
---------------------------------------------
@ -196,17 +202,81 @@ is compiled so recursive creation-dependencies are not possible.
function createAndEndowD(uint arg, uint amount) public payable {
// Send ether along with the creation
D newD = (new D).value(amount)(arg);
D newD = new D{value: amount}(arg);
newD.x();
}
}
As seen in the example, it is possible to send Ether while creating
an instance of ``D`` using the ``.value()`` option, but it is not possible
an instance of ``D`` using the ``value`` option, but it is not possible
to limit the amount of gas.
If the creation fails (due to out-of-stack, not enough balance or other problems),
an exception is thrown.
Salted contract creations / create2
-----------------------------------
When creating a contract, the address of the contract is computed from
the address of the creating contract and a counter that is increased with
each contract creation.
If you specify the option ``salt`` (a bytes32 value), then contract creation will
use a different mechanism to come up with the address of the new contract:
It will compute the address from the address of the creating contract,
the given salt value, the (creation) bytecode of the created contract and the constructor
arguments.
In particular, the counter ("nonce") is not used. This allows for more flexibility
in creating contracts: You are able to derive the address of the
new contract before it is created. Furthermore, you can rely on this address
also in case the creating
contracts creates other contracts in the meantime.
The main use-case here is contracts that act as judges for off-chain interactions,
which only need to be created if there is a dispute.
::
pragma solidity >0.6.1 <0.7.0;
contract D {
uint public x;
constructor(uint a) public {
x = a;
}
}
contract C {
function createDSalted(bytes32 salt, uint arg) public {
/// This complicated expression just tells you how the address
/// can be pre-computed. It is just there for illustration.
/// You actually only need ``new D{salt: salt}(arg)``.
address predictedAddress = address(bytes20(keccak256(abi.encodePacked(
byte(0xff),
address(this),
salt,
keccak256(abi.encodePacked(
type(D).creationCode,
arg
))
))));
D d = new D{salt: salt}(arg);
require(address(d) == predictedAddress);
}
}
.. warning::
There are some peculiarities in relation to salted creation. A contract can be
re-created at the same address after having been destroyed. Yet, it is possible
for that newly created contract to have a different deployed bytecode even
though the creation bytecode has been the same (which is a requirement because
otherwise the address would change). This is due to the fact that the compiler
can query external state that might have changed between the two creations
and incorporate that into the deployed bytecode before it is stored.
Order of Evaluation of Expressions
==================================

View File

@ -164,7 +164,7 @@ The full contract
}
/// destroy the contract and reclaim the leftover funds.
function kill() public {
function shutdown() public {
require(msg.sender == owner);
selfdestruct(msg.sender);
}

View File

@ -24,10 +24,12 @@ StructDefinition = 'struct' Identifier '{'
ModifierDefinition = 'modifier' Identifier ParameterList? ( 'virtual' | OverrideSpecifier )* Block
ModifierInvocation = Identifier ( '(' ExpressionList? ')' )?
FunctionDefinition = 'function' Identifier? ParameterList
FunctionDefinition = FunctionDescriptor ParameterList
( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' | 'virtual' | OverrideSpecifier )*
( 'returns' ParameterList )? ( ';' | Block )
FunctionDescriptor = 'function' Identifier | 'constructor' | 'fallback' | 'receive'
OverrideSpecifier = 'override' ( '(' UserDefinedTypeName (',' UserDefinedTypeName)* ')' )?
EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';'
@ -72,7 +74,7 @@ Statement = IfStatement | TryStatement | WhileStatement | ForStatement | Block |
ExpressionStatement = Expression
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
TryStatement = 'try' Expression ( 'returns' ParameterList )? Block CatchClause+
CatchClause = 'catch' Identifier? ParameterList Block
CatchClause = 'catch' ( Identifier? ParameterList )? Block
WhileStatement = 'while' '(' Expression ')' Statement
PlaceholderStatement = '_'
SimpleStatement = VariableDefinition | ExpressionStatement

View File

@ -104,4 +104,3 @@ Contents
common-patterns.rst
bugs.rst
contributing.rst
lll.rst

View File

@ -174,7 +174,8 @@ Install it using ``brew``:
# eg. Install 0.4.8
brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb
Gentoo Linux also provides a solidity package that can be installed using ``emerge``:
Gentoo Linux has an `Ethereum overlay <https://overlays.gentoo.org/#ethereum>`_ that contains a solidity package.
After the overlay is setup, ``solc`` can be installed in x86_64 architectures by:
.. code-block:: bash

View File

@ -1,21 +0,0 @@
###
LLL
###
.. _lll:
LLL is a low-level language for the EVM with an s-expressions syntax.
The Solidity repository contains an LLL compiler, which shares the assembler subsystem with Solidity.
However, apart from maintaining that it still compiles, no other improvements are made to it.
It is not built unless specifically requested:
.. code-block:: bash
$ cmake -DLLL=ON ..
$ cmake --build .
.. warning::
The LLL codebase is deprecated and will be removed from the Solidity repository in the future.

View File

@ -56,11 +56,11 @@ explanatory purposes.
// Swarm URL is recommended
"urls": [ "bzzr://56ab..." ]
},
"mortal": {
"destructible": {
// Required: keccak256 hash of the source file
"keccak256": "0x234...",
// Required (unless "url" is used): literal contents of the source file
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
"content": "contract destructible is owned { function destroy() { if (msg.sender == owner) selfdestruct(owner); } }"
}
},
// Required: Compiler settings
@ -142,7 +142,7 @@ to the end of the deployed bytecode::
0xa2
0x64 'i' 'p' 'f' 's' 0x58 0x22 <34 bytes IPFS hash>
0x64 's' 'o' 'l' 'c' 0x43 <3 byte version encoding>
0x00 0x32
0x00 0x33
So in order to retrieve the data, the end of the deployed bytecode can be checked
to match that pattern and use the IPFS hash to retrieve the file.

View File

@ -97,23 +97,38 @@ Discontinued:
Solidity Tools
~~~~~~~~~~~~~~
* `ABI to Solidity interface converter <https://gist.github.com/chriseth/8f533d133fa0c15b0d6eaf3ec502c82b>`_
A script for generating contract interfaces from the ABI of a smart contract.
* `Dapp <https://dapp.tools/dapp/>`_
Build tool, package manager, and deployment assistant for Solidity.
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
Try Solidity instantly with a command-line Solidity console.
* `solgraph <https://github.com/raineorshine/solgraph>`_
Visualize Solidity control flow and highlight potential security vulnerabilities.
* `Doxity <https://github.com/DigixGlobal/doxity>`_
Documentation Generator for Solidity.
* `evmdis <https://github.com/Arachnid/evmdis>`_
EVM Disassembler that performs static analysis on the bytecode to provide a higher level of abstraction than raw EVM operations.
* `ABI to solidity interface converter <https://gist.github.com/chriseth/8f533d133fa0c15b0d6eaf3ec502c82b>`_
A script for generating contract interfaces from the ABI of a smart contract.
* `EVM Lab <https://github.com/ethereum/evmlab/>`_
Rich tool package to interact with the EVM. Includes a VM, Etherchain API, and a trace-viewer with gas cost display.
* `leafleth <https://github.com/clemlak/leafleth>`_
A documentation generator for Solidity smart-contracts.
* `PIET <https://piet.slock.it/>`_
A tool to develop, audit and use Solidity smart contracts through a simple graphical interface.
* `solc-select <https://github.com/crytic/solc-select>`_
A script to quickly switch between Solidity compiler versions.
* `Solidity prettier plugin <https://github.com/prettier-solidity/prettier-plugin-solidity>`_
A Prettier Plugin for Solidity.
* `Solidity REPL <https://github.com/raineorshine/solidity-repl>`_
Try Solidity instantly with a command-line Solidity console.
* `solgraph <https://github.com/raineorshine/solgraph>`_
Visualize Solidity control flow and highlight potential security vulnerabilities.
* `Securify <https://securify.ch/>`_
Fully automated online static analyzer for smart contracts, providing a security report based on vulnerability patterns.
@ -121,18 +136,9 @@ Solidity Tools
* `Sūrya <https://github.com/ConsenSys/surya/>`_
Utility tool for smart contract systems, offering a number of visual outputs and information about the contracts' structure. Also supports querying the function call graph.
* `EVM Lab <https://github.com/ethereum/evmlab/>`_
Rich tool package to interact with the EVM. Includes a VM, Etherchain API, and a trace-viewer with gas cost display.
* `Universal Mutator <https://github.com/agroce/universalmutator>`_
A tool for mutation generation, with configurable rules and support for Solidity and Vyper.
* `PIET <https://piet.slock.it/>`_
A tool to develop, audit and use solidity smart contracts through a simple graphical interface.
.. note::
Information like variable names, comments, and source code formatting is lost in the compilation process and it is not possible to completely recover the original source code. Decompiling smart contracts to view the original source code might not be possible, or the end result that useful.
Third-Party Solidity Parsers and Grammars
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -89,7 +89,7 @@ as it uses ``call`` which forwards all remaining gas by default:
mapping(address => uint) shares;
/// Withdraw your share.
function withdraw() public {
(bool success,) = msg.sender.call.value(shares[msg.sender])("");
(bool success,) = msg.sender.call{value: shares[msg.sender]}("");
if (success)
shares[msg.sender] = 0;
}
@ -149,7 +149,7 @@ Sending and Receiving Ether
(for example in the "details" section in Remix).
- There is a way to forward more gas to the receiving contract using
``addr.call.value(x)("")``. This is essentially the same as ``addr.transfer(x)``,
``addr.call{value: x}("")``. This is essentially the same as ``addr.transfer(x)``,
only that it forwards all remaining gas and opens up the ability for the
recipient to perform more expensive actions (and it returns a failure code
instead of automatically propagating the error). This might include calling back

View File

@ -584,7 +584,7 @@ Yes::
return balanceOf[from];
}
function kill() public onlyowner {
function shutdown() public onlyowner {
selfdestruct(owner);
}
@ -594,7 +594,7 @@ No::
return balanceOf[from];
}
function kill() onlyowner public {
function shutdown() onlyowner public {
selfdestruct(owner);
}

View File

@ -276,17 +276,17 @@ Example::
arbitrary arguments and would also handle a first argument of type
``bytes4`` differently. These edge cases were removed in version 0.5.0.
It is possible to adjust the supplied gas with the ``.gas()`` modifier::
It is possible to adjust the supplied gas with the ``gas`` modifier::
address(nameReg).call.gas(1000000)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
Similarly, the supplied Ether value can be controlled too::
address(nameReg).call.value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
Lastly, these modifiers can be combined. Their order does not matter::
address(nameReg).call.gas(1000000).value(1 ether)(abi.encodeWithSignature("register(string)", "MyName"));
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
In a similar way, the function ``delegatecall`` can be used: the difference is that only the code of the given address is used, all other aspects (storage, balance, ...) are taken from the current contract. The purpose of ``delegatecall`` is to use library code which is stored in another contract. The user has to ensure that the layout of storage in both contracts is suitable for delegatecall to be used.
@ -297,7 +297,8 @@ Since byzantium ``staticcall`` can be used as well. This is basically the same a
All three functions ``call``, ``delegatecall`` and ``staticcall`` are very low-level functions and should only be used as a *last resort* as they break the type-safety of Solidity.
The ``.gas()`` option is available on all three methods, while the ``.value()`` option is not supported for ``delegatecall``.
The ``gas`` option is available on all three methods, while the ``value`` option is not
supported for ``delegatecall``.
.. note::
All contracts can be converted to ``address`` type, so it is possible to query the balance of the
@ -635,8 +636,12 @@ External (or public) functions have the following members:
* ``.address`` returns the address of the contract of the function.
* ``.selector`` returns the :ref:`ABI function selector <abi_function_selector>`
* ``.gas(uint)`` returns a callable function object which, when called, will send the specified amount of gas to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
* ``.value(uint)`` returns a callable function object which, when called, will send the specified amount of wei to the target function. See :ref:`External Function Calls <external-function-calls>` for more information.
* ``.gas(uint)`` returns a callable function object which, when called, will send
the specified amount of gas to the target function. Deprecated - use ``{gas: ...}`` instead.
See :ref:`External Function Calls <external-function-calls>` for more information.
* ``.value(uint)`` returns a callable function object which, when called, will
send the specified amount of wei to the target function. Deprecated - use ``{value: ...}`` instead.
See :ref:`External Function Calls <external-function-calls>` for more information.
Example that shows how to use the members::
@ -651,6 +656,8 @@ Example that shows how to use the members::
function g() public {
this.f.gas(10).value(800)();
// New syntax:
// this.f{gas: 10, value: 800}()
}
}

View File

@ -180,12 +180,12 @@ Input Description
// `--allow-paths <path>`.
]
},
"mortal":
"destructible":
{
// Optional: keccak256 hash of the source file
"keccak256": "0x234...",
// Required (unless "urls" is used): literal contents of the source file
"content": "contract mortal is owned { function kill() { if (msg.sender == owner) selfdestruct(owner); } }"
"content": "contract destructible is owned { function shutdown() { if (msg.sender == owner) selfdestruct(owner); } }"
}
},
// Optional
@ -220,8 +220,11 @@ Input Description
"cse": false,
// Optimize representation of literal numbers and strings in code.
"constantOptimizer": false,
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2.
// It can only be activated through the details here.
// The new Yul optimizer. Mostly operates on the code of ABIEncoderV2
// and inline assembly.
// It is activated together with the global optimizer setting
// and can be deactivated here.
// Before Solidity 0.6.0 it had to be activated through this switch.
"yul": false,
// Tuning options for the Yul optimizer.
"yulDetails": {

File diff suppressed because it is too large Load Diff

View File

@ -33,51 +33,10 @@
#include <json/json.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
void Assembly::append(Assembly const& _a)
{
auto newDeposit = m_deposit + _a.deposit();
for (AssemblyItem i: _a.m_items)
{
switch (i.type())
{
case Tag:
case PushTag:
i.setData(i.data() + m_usedTags);
break;
case PushSub:
case PushSubSize:
i.setData(i.data() + m_subs.size());
break;
default:
break;
}
append(i);
}
m_deposit = newDeposit;
m_usedTags += _a.m_usedTags;
// This does not transfer the names of named tags on purpose. The tags themselves are
// transferred, but their names are only available inside the assembly.
for (auto const& i: _a.m_data)
m_data.insert(i);
for (auto const& i: _a.m_strings)
m_strings.insert(i);
m_subs += _a.m_subs;
for (auto const& lib: _a.m_libraries)
m_libraries.insert(lib);
}
void Assembly::append(Assembly const& _a, int _deposit)
{
assertThrow(_deposit <= _a.m_deposit, InvalidDeposit, "");
append(_a);
while (_deposit++ < _a.m_deposit)
append(Instruction::POP);
}
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
using namespace solidity::util;
AssemblyItem const& Assembly::append(AssemblyItem const& _i)
{
@ -87,12 +46,7 @@ AssemblyItem const& Assembly::append(AssemblyItem const& _i)
if (m_items.back().location().isEmpty() && !m_currentSourceLocation.isEmpty())
m_items.back().setLocation(m_currentSourceLocation);
m_items.back().m_modifierDepth = m_currentModifierDepth;
return back();
}
void Assembly::injectStart(AssemblyItem const& _i)
{
m_items.insert(m_items.begin(), _i);
return m_items.back();
}
unsigned Assembly::bytesRequired(unsigned subTagSize) const
@ -105,7 +59,7 @@ unsigned Assembly::bytesRequired(unsigned subTagSize) const
for (AssemblyItem const& i: m_items)
ret += i.bytesRequired(tagSize);
if (dev::bytesRequired(ret) <= tagSize)
if (util::bytesRequired(ret) <= tagSize)
return ret;
}
}
@ -290,15 +244,15 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
createJsonValue("PUSH [ErrorTag]", i.location().start, i.location().end, ""));
else
collection.append(
createJsonValue("PUSH [tag]", i.location().start, i.location().end, dev::toString(i.data())));
createJsonValue("PUSH [tag]", i.location().start, i.location().end, toString(i.data())));
break;
case PushSub:
collection.append(
createJsonValue("PUSH [$]", i.location().start, i.location().end, dev::toString(h256(i.data()))));
createJsonValue("PUSH [$]", i.location().start, i.location().end, toString(h256(i.data()))));
break;
case PushSubSize:
collection.append(
createJsonValue("PUSH #[$]", i.location().start, i.location().end, dev::toString(h256(i.data()))));
createJsonValue("PUSH #[$]", i.location().start, i.location().end, toString(h256(i.data()))));
break;
case PushProgramSize:
collection.append(
@ -316,7 +270,7 @@ Json::Value Assembly::assemblyJSON(StringMap const& _sourceCodes) const
break;
case Tag:
collection.append(
createJsonValue("tag", i.location().start, i.location().end, dev::toString(i.data())));
createJsonValue("tag", i.location().start, i.location().end, toString(i.data())));
collection.append(
createJsonValue("JUMPDEST", i.location().start, i.location().end));
break;
@ -359,7 +313,7 @@ AssemblyItem Assembly::namedTag(string const& _name)
AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier)
{
h256 h(dev::keccak256(_identifier));
h256 h(util::keccak256(_identifier));
m_libraries[h] = _identifier;
return AssemblyItem{PushLibraryAddress, h};
}
@ -543,14 +497,14 @@ LinkerObject const& Assembly::assemble() const
multimap<h256, unsigned> dataRef;
multimap<size_t, size_t> subRef;
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode);
unsigned bytesPerTag = util::bytesRequired(bytesRequiredForCode);
uint8_t tagPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerTag;
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1 + m_auxiliaryData.size();
for (auto const& sub: m_subs)
bytesRequiredIncludingData += sub->assemble().bytecode.size();
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);
unsigned bytesPerDataRef = util::bytesRequired(bytesRequiredIncludingData);
uint8_t dataRefPush = (uint8_t)Instruction::PUSH1 - 1 + bytesPerDataRef;
ret.bytecode.reserve(bytesRequiredIncludingData);
@ -580,7 +534,7 @@ LinkerObject const& Assembly::assemble() const
}
case Push:
{
uint8_t b = max<unsigned>(1, dev::bytesRequired(i.data()));
uint8_t b = max<unsigned>(1, util::bytesRequired(i.data()));
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
ret.bytecode.resize(ret.bytecode.size() + b);
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
@ -610,7 +564,7 @@ LinkerObject const& Assembly::assemble() const
assertThrow(i.data() <= size_t(-1), AssemblyException, "");
auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size();
i.setPushedValue(u256(s));
uint8_t b = max<unsigned>(1, dev::bytesRequired(s));
uint8_t b = max<unsigned>(1, util::bytesRequired(s));
ret.bytecode.push_back((uint8_t)Instruction::PUSH1 - 1 + b);
ret.bytecode.resize(ret.bytecode.size() + b);
bytesRef byr(&ret.bytecode.back() + 1 - b, b);
@ -675,7 +629,7 @@ LinkerObject const& Assembly::assemble() const
assertThrow(tagId < tagPositions.size(), AssemblyException, "Reference to non-existing tag.");
size_t pos = tagPositions[tagId];
assertThrow(pos != size_t(-1), AssemblyException, "Reference to tag without position.");
assertThrow(dev::bytesRequired(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space.");
assertThrow(util::bytesRequired(pos) <= bytesPerTag, AssemblyException, "Tag too large for reserved space.");
bytesRef r(ret.bytecode.data() + i.first, bytesPerTag);
toBigEndian(pos, r);
}

View File

@ -25,9 +25,9 @@
#include <liblangutil/EVMVersion.h>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/Keccak256.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/Keccak256.h>
#include <json/json.h>
@ -35,9 +35,7 @@
#include <sstream>
#include <memory>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
using AssemblyPointer = std::shared_ptr<Assembly>;
@ -49,8 +47,8 @@ public:
AssemblyItem newPushTag() { assertThrow(m_usedTags < 0xffffffff, AssemblyException, ""); return AssemblyItem(PushTag, m_usedTags++); }
/// Returns a tag identified by the given name. Creates it if it does not yet exist.
AssemblyItem namedTag(std::string const& _name);
AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
bytes const& data(h256 const& _i) const { return m_data.at(_i); }
AssemblyItem newData(bytes const& _data) { util::h256 h(util::keccak256(util::asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
bytes const& data(util::h256 const& _i) const { return m_data.at(_i); }
AssemblyItem newSub(AssemblyPointer const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
Assembly const& sub(size_t _sub) const { return *m_subs.at(_sub); }
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
@ -58,7 +56,6 @@ public:
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
AssemblyItem const& append(AssemblyItem const& _i);
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
template <class T> Assembly& operator<<(T const& _d) { append(_d); return *this; }
@ -139,18 +136,6 @@ public:
StringMap const& _sourceCodes = StringMap()
) const;
public:
// These features are only used by LLL
AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); }
void append(Assembly const& _a);
void append(Assembly const& _a, int _deposit);
void injectStart(AssemblyItem const& _i);
AssemblyItem const& back() const { return m_items.back(); }
std::string backString() const { return m_items.size() && m_items.back().type() == PushString ? m_strings.at((h256)m_items.back().data()) : std::string(); }
protected:
/// Does the same operations as @a optimise, but should only be applied to a sub and
/// returns the replaced tags. Also takes an argument containing the tags of this assembly
@ -168,12 +153,12 @@ protected:
unsigned m_usedTags = 1;
std::map<std::string, size_t> m_namedTags;
AssemblyItems m_items;
std::map<h256, bytes> m_data;
std::map<util::h256, bytes> m_data;
/// Data that is appended to the very end of the contract.
bytes m_auxiliaryData;
std::vector<std::shared_ptr<Assembly>> m_subs;
std::map<h256, std::string> m_strings;
std::map<h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
std::map<util::h256, std::string> m_strings;
std::map<util::h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
mutable LinkerObject m_assembledObject;
mutable std::vector<size_t> m_tagPositionsInBytecode;
@ -192,4 +177,3 @@ inline std::ostream& operator<<(std::ostream& _out, Assembly const& _a)
}
}
}

View File

@ -17,21 +17,21 @@
#include <libevmasm/AssemblyItem.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/FixedHash.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/FixedHash.h>
#include <fstream>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
static_assert(sizeof(size_t) <= 8, "size_t must be at most 64-bits wide");
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
{
assertThrow(data() < (u256(1) << 64), Exception, "Tag already has subassembly set.");
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
assertThrow(data() < (u256(1) << 64), util::Exception, "Tag already has subassembly set.");
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
size_t tag = size_t(u256(data()) & 0xffffffffffffffffULL);
AssemblyItem r = *this;
r.m_type = PushTag;
@ -41,7 +41,7 @@ AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
{
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
u256 combined = u256(data());
size_t subId = size_t((combined >> 64) - 1);
size_t tag = size_t(combined & 0xffffffffffffffffULL);
@ -50,7 +50,7 @@ pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
{
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
assertThrow(m_type == PushTag || m_type == Tag, util::Exception, "");
u256 data = _tag;
if (_subId != size_t(-1))
data |= (u256(_subId) + 1) << 64;
@ -67,7 +67,7 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
case PushString:
return 1 + 32;
case Push:
return 1 + max<unsigned>(1, dev::bytesRequired(data()));
return 1 + max<unsigned>(1, util::bytesRequired(data()));
case PushSubSize:
case PushProgramSize:
return 1 + 4; // worst case: a 16MB program
@ -170,10 +170,10 @@ string AssemblyItem::toAssemblyText() const
break;
}
case Push:
text = toHex(toCompactBigEndian(data(), 1), HexPrefix::Add);
text = toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add);
break;
case PushString:
text = string("data_") + toHex(data());
text = string("data_") + util::toHex(data());
break;
case PushTag:
{
@ -191,7 +191,7 @@ string AssemblyItem::toAssemblyText() const
text = string("tag_") + to_string(size_t(data())) + ":";
break;
case PushData:
text = string("data_") + toHex(data());
text = string("data_") + util::toHex(data());
break;
case PushSub:
text = string("dataOffset(sub_") + to_string(size_t(data())) + ")";
@ -203,7 +203,7 @@ string AssemblyItem::toAssemblyText() const
text = string("bytecodeSize");
break;
case PushLibraryAddress:
text = string("linkerSymbol(\"") + toHex(data()) + string("\")");
text = string("linkerSymbol(\"") + util::toHex(data()) + string("\")");
break;
case PushDeployTimeAddress:
text = string("deployTimeAddress()");
@ -225,7 +225,7 @@ string AssemblyItem::toAssemblyText() const
return text;
}
ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item)
{
switch (_item.type())
{
@ -266,7 +266,7 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
break;
case PushLibraryAddress:
{
string hash(h256((_item.data())).hex());
string hash(util::h256((_item.data())).hex());
_out << " PushLibraryAddress " << hash.substr(0, 8) + "..." + hash.substr(hash.length() - 8);
break;
}

View File

@ -24,14 +24,12 @@
#include <libevmasm/Instruction.h>
#include <libevmasm/Exceptions.h>
#include <liblangutil/SourceLocation.h>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <iostream>
#include <sstream>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
enum AssemblyItemType {
@ -77,8 +75,8 @@ public:
AssemblyItem& operator=(AssemblyItem const&) = default;
AssemblyItem& operator=(AssemblyItem&&) = default;
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, data()); }
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, data()); }
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(Tag, data()); }
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, util::Exception, ""); return AssemblyItem(PushTag, data()); }
/// Converts the tag to a subassembly tag. This has to be called in order to move a tag across assemblies.
/// @param _subId the identifier of the subassembly the tag is taken from.
AssemblyItem toSubAssemblyTag(size_t _subId) const;
@ -89,11 +87,11 @@ public:
void setPushTagSubIdAndTag(size_t _subId, size_t _tag);
AssemblyItemType type() const { return m_type; }
u256 const& data() const { assertThrow(m_type != Operation, Exception, ""); return *m_data; }
void setData(u256 const& _data) { assertThrow(m_type != Operation, Exception, ""); m_data = std::make_shared<u256>(_data); }
u256 const& data() const { assertThrow(m_type != Operation, util::Exception, ""); return *m_data; }
void setData(u256 const& _data) { assertThrow(m_type != Operation, util::Exception, ""); m_data = std::make_shared<u256>(_data); }
/// @returns the instruction of this item (only valid if type() == Operation)
Instruction instruction() const { assertThrow(m_type == Operation, Exception, ""); return m_instruction; }
Instruction instruction() const { assertThrow(m_type == Operation, util::Exception, ""); return m_instruction; }
/// @returns true if the type and data of the items are equal.
bool operator==(AssemblyItem const& _other) const
@ -178,4 +176,3 @@ inline std::ostream& operator<<(std::ostream& _out, AssemblyItems const& _items)
}
}
}

View File

@ -30,8 +30,8 @@
#include <set>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
bool BlockDeduplicator::deduplicate()

View File

@ -23,16 +23,14 @@
#pragma once
#include <libdevcore/Common.h>
#include <libsolutil/Common.h>
#include <cstddef>
#include <vector>
#include <functional>
#include <map>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
@ -90,4 +88,3 @@ private:
};
}
}

View File

@ -36,4 +36,4 @@ set(sources
)
add_library(evmasm ${sources})
target_link_libraries(evmasm PUBLIC devcore)
target_link_libraries(evmasm PUBLIC solutil)

View File

@ -23,14 +23,14 @@
#include <functional>
#include <boost/range/adaptor/reversed.hpp>
#include <libdevcore/Keccak256.h>
#include <libsolutil/Keccak256.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/AssemblyItem.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems()
{

View File

@ -28,8 +28,8 @@
#include <set>
#include <tuple>
#include <ostream>
#include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h>
#include <libsolutil/CommonIO.h>
#include <libsolutil/Exceptions.h>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SemanticInformation.h>
#include <libevmasm/KnownState.h>
@ -39,9 +39,7 @@ namespace langutil
struct SourceLocation;
}
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
@ -187,4 +185,3 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
}
}
}

View File

@ -22,9 +22,11 @@
#include <libevmasm/ConstantOptimiser.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/GasMeter.h>
#include <libsolutil/CommonData.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
unsigned ConstantOptimisationMethod::optimiseConstants(
bool _isCreation,
@ -101,7 +103,7 @@ bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items)
{
return eth::bytesRequired(_items, 3); // assume 3 byte addresses
return evmasm::bytesRequired(_items, 3); // assume 3 byte addresses
}
void ConstantOptimisationMethod::replaceConstants(
@ -131,7 +133,7 @@ bigint LiteralMethod::gasNeeded() const
return combineGas(
simpleRunGas({Instruction::PUSH1}),
// PUSHX plus data
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(toCompactBigEndian(m_value, 1)),
(m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas) + dataGas(util::toCompactBigEndian(m_value, 1)),
0
);
}
@ -144,13 +146,13 @@ bigint CodeCopyMethod::gasNeeded() const
// Data gas for copy routines: Some bytes are zero, but we ignore them.
bytesRequired(copyRoutine()) * (m_params.isCreation ? GasCosts::txDataNonZeroGas(m_params.evmVersion) : GasCosts::createDataGas),
// Data gas for data itself
dataGas(toBigEndian(m_value))
dataGas(util::toBigEndian(m_value))
);
}
AssemblyItems CodeCopyMethod::execute(Assembly& _assembly) const
{
bytes data = toBigEndian(m_value);
bytes data = util::toBigEndian(m_value);
assertThrow(data.size() == 32, OptimizerException, "Invalid number encoding.");
AssemblyItems actualCopyRoutine = copyRoutine();
actualCopyRoutine[4] = _assembly.newData(data);
@ -190,7 +192,7 @@ AssemblyItems ComputeMethod::findRepresentation(u256 const& _value)
if (_value < 0x10000)
// Very small value, not worth computing
return AssemblyItems{_value};
else if (dev::bytesRequired(~_value) < dev::bytesRequired(_value))
else if (util::bytesRequired(~_value) < util::bytesRequired(_value))
// Negated is shorter to represent
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT};
else

View File

@ -25,15 +25,13 @@
#include <liblangutil/EVMVersion.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/CommonIO.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/CommonIO.h>
#include <vector>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
@ -164,4 +162,3 @@ protected:
};
}
}

View File

@ -31,8 +31,8 @@
#include <libevmasm/KnownState.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
BlockId::BlockId(u256 const& _id):
m_id(unsigned(_id))

View File

@ -25,13 +25,11 @@
#include <vector>
#include <memory>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <libevmasm/ExpressionClasses.h>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class KnownState;
@ -127,4 +125,3 @@ private:
}
}

View File

@ -21,14 +21,12 @@
#pragma once
#include <libdevcore/Exceptions.h>
#include <libsolutil/Exceptions.h>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
struct AssemblyException: virtual Exception {};
struct AssemblyException: virtual util::Exception {};
struct OptimizerException: virtual AssemblyException {};
struct StackTooDeepException: virtual OptimizerException {};
struct ItemNotAvailableException: virtual OptimizerException {};
@ -37,4 +35,3 @@ DEV_SIMPLE_EXCEPTION(InvalidDeposit);
DEV_SIMPLE_EXCEPTION(InvalidOpcode);
}
}

View File

@ -32,9 +32,9 @@
#include <libevmasm/SimplificationRules.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
bool ExpressionClasses::Expression::operator<(ExpressionClasses::Expression const& _other) const
{

View File

@ -23,7 +23,7 @@
#pragma once
#include <libdevcore/Common.h>
#include <libsolutil/Common.h>
#include <libevmasm/AssemblyItem.h>
#include <vector>
@ -31,14 +31,12 @@
#include <memory>
#include <set>
namespace langutil
namespace solidity::langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class Pattern;
@ -128,4 +126,3 @@ private:
};
}
}

View File

@ -19,11 +19,10 @@
#include <libevmasm/KnownState.h>
#include <libdevcore/FixedHash.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption const& _other)
{
@ -181,7 +180,14 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _
case Instruction::EXP:
gas = GasCosts::expGas;
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(-1)))
gas += GasCosts::expByteGas(m_evmVersion) * (32 - (h256(*value).firstBitSet() / 8));
{
if (*value)
{
// Note: msb() counts from 0 and throws on 0 as input.
unsigned const significantByteCount = (boost::multiprecision::msb(*value) + 1 + 7) / 8;
gas += GasCosts::expByteGas(m_evmVersion) * significantByteCount;
}
}
else
gas += GasCosts::expByteGas(m_evmVersion) * 32;
break;

View File

@ -29,9 +29,7 @@
#include <ostream>
#include <tuple>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class KnownState;
@ -180,4 +178,3 @@ inline std::ostream& operator<<(std::ostream& _str, GasMeter::GasConsumption con
}
}

View File

@ -21,16 +21,17 @@
#include <libevmasm/Instruction.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libsolutil/Common.h>
#include <libsolutil/CommonIO.h>
#include <algorithm>
#include <functional>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
std::map<std::string, Instruction> const dev::eth::c_instructions =
std::map<std::string, Instruction> const solidity::evmasm::c_instructions =
{
{ "STOP", Instruction::STOP },
{ "ADD", Instruction::ADD },
@ -322,7 +323,7 @@ static std::map<Instruction, InstructionInfo> const c_instructionInfo =
{ Instruction::SELFDESTRUCT, { "SELFDESTRUCT", 0, 1, 0, true, Tier::Special } }
};
void dev::eth::eachInstruction(
void solidity::evmasm::eachInstruction(
bytes const& _mem,
function<void(Instruction,u256 const&)> const& _onInstruction
)
@ -351,7 +352,7 @@ void dev::eth::eachInstruction(
}
}
string dev::eth::disassemble(bytes const& _mem)
string solidity::evmasm::disassemble(bytes const& _mem)
{
stringstream ret;
eachInstruction(_mem, [&](Instruction _instr, u256 const& _data) {
@ -368,7 +369,7 @@ string dev::eth::disassemble(bytes const& _mem)
return ret.str();
}
InstructionInfo dev::eth::instructionInfo(Instruction _inst)
InstructionInfo solidity::evmasm::instructionInfo(Instruction _inst)
{
try
{
@ -380,7 +381,7 @@ InstructionInfo dev::eth::instructionInfo(Instruction _inst)
}
}
bool dev::eth::isValidInstruction(Instruction _inst)
bool solidity::evmasm::isValidInstruction(Instruction _inst)
{
return !!c_instructionInfo.count(_inst);
}

View File

@ -22,13 +22,11 @@
#pragma once
#include <libevmasm/Exceptions.h>
#include <libdevcore/Common.h>
#include <libdevcore/Assertions.h>
#include <libsolutil/Common.h>
#include <libsolutil/Assertions.h>
#include <functional>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
/// Virtual machine bytecode instruction.
@ -318,4 +316,3 @@ void eachInstruction(bytes const& _mem, std::function<void(Instruction,u256 cons
std::string disassemble(bytes const& _mem);
}
}

View File

@ -24,9 +24,9 @@
#include <libevmasm/AssemblyItem.h>
using namespace std;
using namespace dev::eth;
using namespace dev;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
bool JumpdestRemover::optimise(set<size_t> const& _tagsReferencedFromOutside)
{

View File

@ -24,9 +24,7 @@
#include <cstddef>
#include <set>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
@ -47,4 +45,3 @@ private:
};
}
}

View File

@ -23,14 +23,14 @@
#include <libevmasm/KnownState.h>
#include <libevmasm/AssemblyItem.h>
#include <libdevcore/Keccak256.h>
#include <libsolutil/Keccak256.h>
#include <functional>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
ostream& KnownState::stream(ostream& _out) const
{
@ -383,9 +383,9 @@ KnownState::Id KnownState::applyKeccak256(
{
bytes data;
for (Id a: arguments)
data += toBigEndian(*m_expressionClasses->knownConstant(a));
data += util::toBigEndian(*m_expressionClasses->knownConstant(a));
data.resize(size_t(*l));
v = m_expressionClasses->find(AssemblyItem(u256(dev::keccak256(data)), _location));
v = m_expressionClasses->find(AssemblyItem(u256(util::keccak256(data)), _location));
}
else
v = m_expressionClasses->find(keccak256Item, {_start, _length}, true, m_sequenceNumber);

View File

@ -41,19 +41,17 @@
#pragma clang diagnostic pop
#endif // defined(__clang__)
#include <libdevcore/CommonIO.h>
#include <libdevcore/Exceptions.h>
#include <libsolutil/CommonIO.h>
#include <libsolutil/Exceptions.h>
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SemanticInformation.h>
namespace langutil
namespace solidity::langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
@ -182,4 +180,3 @@ private:
};
}
}

View File

@ -20,12 +20,13 @@
*/
#include <libevmasm/LinkerObject.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/Keccak256.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Keccak256.h>
using namespace dev;
using namespace dev::eth;
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::evmasm;
void LinkerObject::append(LinkerObject const& _other)
{
@ -47,7 +48,7 @@ void LinkerObject::link(map<string, h160> const& _libraryAddresses)
string LinkerObject::toHex() const
{
string hex = dev::toHex(bytecode);
string hex = solidity::util::toHex(bytecode);
for (auto const& ref: linkReferences)
{
size_t pos = ref.first * 2;

View File

@ -21,12 +21,10 @@
#pragma once
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libsolutil/Common.h>
#include <libsolutil/FixedHash.h>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
/**
@ -46,7 +44,7 @@ struct LinkerObject
void append(LinkerObject const& _other);
/// Links the given libraries by replacing their uses in the code and removes them from the references.
void link(std::map<std::string, h160> const& _libraryAddresses);
void link(std::map<std::string, util::h160> const& _libraryAddresses);
/// @returns a hex representation of the bytecode of the given object, replacing unlinked
/// addresses by placeholders. This output is lowercase.
@ -58,11 +56,10 @@ struct LinkerObject
static std::string libraryPlaceholder(std::string const& _libraryName);
private:
static h160 const* matchLibrary(
static util::h160 const* matchLibrary(
std::string const& _linkRefName,
std::map<std::string, h160> const& _libraryAddresses
std::map<std::string, util::h160> const& _libraryAddresses
);
};
}
}

View File

@ -24,8 +24,8 @@
#include <libevmasm/SemanticInformation.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
PathGasMeter::PathGasMeter(AssemblyItems const& _items, langutil::EVMVersion _evmVersion):
m_items(_items), m_evmVersion(_evmVersion)

View File

@ -29,9 +29,7 @@
#include <vector>
#include <memory>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class KnownState;
@ -85,4 +83,3 @@ private:
};
}
}

View File

@ -25,8 +25,8 @@
#include <libevmasm/SemanticInformation.h>
using namespace std;
using namespace dev::eth;
using namespace dev;
using namespace solidity;
using namespace solidity::evmasm;
// TODO: Extend this to use the tools from ExpressionClasses.cpp
@ -362,7 +362,7 @@ bool PeepholeOptimiser::optimise()
);
if (m_optimisedItems.size() < m_items.size() || (
m_optimisedItems.size() == m_items.size() && (
eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) ||
evmasm::bytesRequired(m_optimisedItems, 3) < evmasm::bytesRequired(m_items, 3) ||
numberOfPops(m_optimisedItems) > numberOfPops(m_items)
)
))

View File

@ -24,9 +24,7 @@
#include <cstddef>
#include <iterator>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
using AssemblyItems = std::vector<AssemblyItem>;
@ -53,4 +51,3 @@ private:
};
}
}

View File

@ -25,16 +25,14 @@
#include <libevmasm/Instruction.h>
#include <libevmasm/SimplificationRule.h>
#include <libdevcore/CommonData.h>
#include <libsolutil/CommonData.h>
#include <boost/multiprecision/detail/min_max.hpp>
#include <vector>
#include <functional>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
template <class S> S divWorkaround(S const& _a, S const& _b)
@ -674,4 +672,3 @@ std::vector<SimplificationRule<Pattern>> simplificationRuleList(
}
}
}

View File

@ -25,8 +25,8 @@
#include <libevmasm/AssemblyItem.h>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
{
@ -96,14 +96,14 @@ bool SemanticInformation::isDupInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
return dev::eth::isDupInstruction(_item.instruction());
return evmasm::isDupInstruction(_item.instruction());
}
bool SemanticInformation::isSwapInstruction(AssemblyItem const& _item)
{
if (_item.type() != Operation)
return false;
return dev::eth::isSwapInstruction(_item.instruction());
return evmasm::isSwapInstruction(_item.instruction());
}
bool SemanticInformation::isJumpInstruction(AssemblyItem const& _item)

View File

@ -25,9 +25,7 @@
#include <libevmasm/Instruction.h>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class AssemblyItem;
@ -75,4 +73,3 @@ struct SemanticInformation
};
}
}

View File

@ -21,12 +21,10 @@
#pragma once
#include <libevmasm/Instruction.h>
#include <libdevcore/CommonData.h>
#include <libsolutil/CommonData.h>
#include <functional>
namespace dev
{
namespace eth
namespace solidity::evmasm
{
/**
@ -67,7 +65,7 @@ struct EVMBuiltins
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
{
return {inst, {std::forward<Args>(_args)...}};
};
}
};
struct PatternGeneratorInstance
@ -76,7 +74,7 @@ struct EVMBuiltins
template<typename... Args> constexpr Pattern operator()(Args&&... _args) const
{
return {instruction, {std::forward<Args>(_args)...}};
};
}
};
@ -158,4 +156,3 @@ struct EVMBuiltins
};
}
}

View File

@ -27,7 +27,7 @@
#include <libevmasm/Assembly.h>
#include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/RuleList.h>
#include <libdevcore/Assertions.h>
#include <libsolutil/Assertions.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
@ -36,9 +36,9 @@
#include <functional>
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace langutil;
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
SimplificationRule<Pattern> const* Rules::findFirstMatch(
Expression const& _expr,

View File

@ -26,21 +26,19 @@
#include <libevmasm/ExpressionClasses.h>
#include <libevmasm/SimplificationRule.h>
#include <libdevcore/CommonData.h>
#include <libsolutil/CommonData.h>
#include <boost/noncopyable.hpp>
#include <functional>
#include <vector>
namespace langutil
namespace solidity::langutil
{
struct SourceLocation;
}
namespace dev
{
namespace eth
namespace solidity::evmasm
{
class Pattern;
@ -89,7 +87,7 @@ public:
using Expression = ExpressionClasses::Expression;
using Id = ExpressionClasses::Id;
using Builtins = dev::eth::EVMBuiltins<Pattern>;
using Builtins = evmasm::EVMBuiltins<Pattern>;
static constexpr size_t WordSize = 256;
using Word = u256;
@ -159,4 +157,3 @@ struct ExpressionTemplate
};
}
}

View File

@ -16,6 +16,7 @@ set(sources
SemVerHandler.cpp
SemVerHandler.h
SourceLocation.h
SourceLocation.cpp
SourceReferenceExtractor.cpp
SourceReferenceExtractor.h
SourceReferenceFormatter.cpp
@ -28,4 +29,4 @@ set(sources
)
add_library(langutil ${sources})
target_link_libraries(langutil PUBLIC devcore)
target_link_libraries(langutil PUBLIC solutil)

View File

@ -54,7 +54,8 @@
#include <liblangutil/Exceptions.h>
using namespace std;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
char CharStream::advanceAndGet(size_t _chars)
{

View File

@ -56,7 +56,7 @@
#include <string>
#include <tuple>
namespace langutil
namespace solidity::langutil
{
/**

View File

@ -14,7 +14,7 @@
#pragma once
namespace langutil
namespace solidity::langutil
{
inline bool isDecimalDigit(char c)

View File

@ -20,8 +20,9 @@
#include <liblangutil/EVMVersion.h>
using namespace langutil;
using namespace dev::eth;
using namespace solidity;
using namespace solidity::evmasm;
using namespace solidity::langutil;
bool EVMVersion::hasOpcode(Instruction _opcode) const
{

View File

@ -28,7 +28,7 @@
#include <boost/operators.hpp>
namespace langutil
namespace solidity::langutil
{
/**
@ -87,7 +87,7 @@ public:
bool hasChainID() const { return *this >= istanbul(); }
bool hasSelfBalance() const { return *this >= istanbul(); }
bool hasOpcode(dev::eth::Instruction _opcode) const;
bool hasOpcode(evmasm::Instruction _opcode) const;
/// Whether we have to retain the costs for the call opcode itself (false),
/// or whether we can just forward easily all remaining gas (true).

View File

@ -25,8 +25,8 @@
#include <memory>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
ErrorReporter& ErrorReporter::operator=(ErrorReporter const& _errorReporter)
{
@ -67,7 +67,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, st
auto err = make_shared<Error>(_type);
*err <<
errinfo_sourceLocation(_location) <<
errinfo_comment(_description);
util::errinfo_comment(_description);
m_errorList.push_back(err);
}
@ -81,7 +81,7 @@ void ErrorReporter::error(Error::Type _type, SourceLocation const& _location, Se
*err <<
errinfo_sourceLocation(_location) <<
errinfo_secondarySourceLocation(_secondaryLocation) <<
errinfo_comment(_description);
util::errinfo_comment(_description);
m_errorList.push_back(err);
}
@ -100,7 +100,7 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
if (m_warningCount == c_maxWarningsAllowed)
{
auto err = make_shared<Error>(Error::Type::Warning);
*err << errinfo_comment("There are more than 256 warnings. Ignoring the rest.");
*err << util::errinfo_comment("There are more than 256 warnings. Ignoring the rest.");
m_errorList.push_back(err);
}
@ -114,7 +114,7 @@ bool ErrorReporter::checkForExcessiveErrors(Error::Type _type)
if (m_errorCount > c_maxErrorsAllowed)
{
auto err = make_shared<Error>(Error::Type::Warning);
*err << errinfo_comment("There are more than 256 errors. Aborting.");
*err << util::errinfo_comment("There are more than 256 errors. Aborting.");
m_errorList.push_back(err);
BOOST_THROW_EXCEPTION(FatalError());
}

View File

@ -22,15 +22,15 @@
#pragma once
#include <libdevcore/CommonData.h>
#include <libsolutil/CommonData.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceLocation.h>
#include <libdevcore/StringUtils.h>
#include <libsolutil/StringUtils.h>
#include <boost/range/adaptor/filtered.hpp>
namespace langutil
namespace solidity::langutil
{
class ErrorReporter
@ -98,7 +98,7 @@ public:
auto filterEmpty = boost::adaptors::filtered([](std::string const& _s) { return !_s.empty(); });
std::string errorStr = dev::joinHumanReadable(descs | filterEmpty, " ");
std::string errorStr = util::joinHumanReadable(descs | filterEmpty, " ");
error(Error::Type::TypeError, _location, errorStr);
}

View File

@ -23,8 +23,8 @@
#include <liblangutil/Exceptions.h>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
Error::Error(Type _type, SourceLocation const& _location, string const& _description):
m_type(_type)
@ -54,7 +54,7 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
if (!_location.isEmpty())
*this << errinfo_sourceLocation(_location);
if (!_description.empty())
*this << errinfo_comment(_description);
*this << util::errinfo_comment(_description);
}
Error::Error(Error::Type _type, std::string const& _description, SourceLocation const& _location):
@ -62,5 +62,5 @@ Error::Error(Error::Type _type, std::string const& _description, SourceLocation
{
if (!_location.isEmpty())
*this << errinfo_sourceLocation(_location);
*this << errinfo_comment(_description);
*this << util::errinfo_comment(_description);
}

View File

@ -26,32 +26,36 @@
#include <utility>
#include <vector>
#include <memory>
#include <libdevcore/Exceptions.h>
#include <libdevcore/Assertions.h>
#include <libdevcore/CommonData.h>
#include <libsolutil/Exceptions.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/CommonData.h>
#include <liblangutil/SourceLocation.h>
namespace langutil
namespace solidity::langutil
{
class Error;
using ErrorList = std::vector<std::shared_ptr<Error const>>;
struct CompilerError: virtual dev::Exception {};
struct InternalCompilerError: virtual dev::Exception {};
struct FatalError: virtual dev::Exception {};
struct UnimplementedFeatureError: virtual dev::Exception {};
struct CompilerError: virtual util::Exception {};
struct InternalCompilerError: virtual util::Exception {};
struct FatalError: virtual util::Exception {};
struct UnimplementedFeatureError: virtual util::Exception {};
struct InvalidAstError: virtual util::Exception {};
/// Assertion that throws an InternalCompilerError containing the given description if it is not met.
#define solAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::langutil::InternalCompilerError, DESCRIPTION)
assertThrow(CONDITION, ::solidity::langutil::InternalCompilerError, DESCRIPTION)
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::langutil::UnimplementedFeatureError, DESCRIPTION)
assertThrow(CONDITION, ::solidity::langutil::UnimplementedFeatureError, DESCRIPTION)
#define solUnimplemented(DESCRIPTION) \
solUnimplementedAssert(false, DESCRIPTION)
class Error: virtual public dev::Exception
#define astAssert(CONDITION, DESCRIPTION) \
assertThrow(CONDITION, ::solidity::langutil::InvalidAstError, DESCRIPTION)
class Error: virtual public util::Exception
{
public:
enum class Type

View File

@ -25,7 +25,8 @@
#include <liblangutil/ErrorReporter.h>
using namespace std;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
int ParserBase::position() const
{

View File

@ -27,7 +27,7 @@
#include <memory>
#include <string>
namespace langutil
namespace solidity::langutil
{
class ErrorReporter;

View File

@ -60,9 +60,10 @@
#include <tuple>
using namespace std;
using namespace langutil;
string langutil::to_string(ScannerError _errorCode)
namespace solidity::langutil {
string to_string(ScannerError _errorCode)
{
switch (_errorCode)
{
@ -84,14 +85,11 @@ string langutil::to_string(ScannerError _errorCode)
}
ostream& langutil::operator<<(ostream& os, ScannerError _errorCode)
ostream& operator<<(ostream& os, ScannerError _errorCode)
{
return os << to_string(_errorCode);
}
namespace langutil
{
/// Scoped helper for literal recording. Automatically drops the literal
/// if aborting the scanning before it's complete.
enum LiteralType
@ -110,18 +108,18 @@ public:
m_complete(false)
{
if (_type == LITERAL_TYPE_COMMENT)
m_scanner->m_nextSkippedComment.literal.clear();
m_scanner->m_skippedComments[Scanner::NextNext].literal.clear();
else
m_scanner->m_nextToken.literal.clear();
m_scanner->m_tokens[Scanner::NextNext].literal.clear();
}
~LiteralScope()
{
if (!m_complete)
{
if (m_type == LITERAL_TYPE_COMMENT)
m_scanner->m_nextSkippedComment.literal.clear();
m_scanner->m_skippedComments[Scanner::NextNext].literal.clear();
else
m_scanner->m_nextToken.literal.clear();
m_scanner->m_tokens[Scanner::NextNext].literal.clear();
}
}
void complete() { m_complete = true; }
@ -132,8 +130,6 @@ private:
bool m_complete;
};
}
void Scanner::reset(CharStream _source)
{
m_source = make_shared<CharStream>(std::move(_source));
@ -155,6 +151,7 @@ void Scanner::reset()
skipWhitespace();
next();
next();
next();
}
void Scanner::setPosition(size_t _offset)
@ -162,6 +159,7 @@ void Scanner::setPosition(size_t _offset)
m_char = m_source->setPosition(_offset);
scanToken();
next();
next();
}
void Scanner::supportPeriodInIdentifier(bool _value)
@ -226,13 +224,14 @@ void Scanner::addUnicodeAsUTF8(unsigned codepoint)
void Scanner::rescan()
{
size_t rollbackTo = 0;
if (m_skippedComment.literal.empty())
rollbackTo = m_currentToken.location.start;
if (m_skippedComments[Current].literal.empty())
rollbackTo = m_tokens[Current].location.start;
else
rollbackTo = m_skippedComment.location.start;
rollbackTo = m_skippedComments[Current].location.start;
m_char = m_source->rollback(size_t(m_source->position()) - rollbackTo);
next();
next();
next();
}
// Ensure that tokens can be stored in a byte.
@ -240,11 +239,14 @@ BOOST_STATIC_ASSERT(TokenTraits::count() <= 0x100);
Token Scanner::next()
{
m_currentToken = m_nextToken;
m_skippedComment = m_nextSkippedComment;
m_tokens[Current] = std::move(m_tokens[Next]);
m_tokens[Next] = std::move(m_tokens[NextNext]);
m_skippedComments[Current] = std::move(m_skippedComments[Next]);
m_skippedComments[Next] = std::move(m_skippedComments[NextNext]);
scanToken();
return m_currentToken.token;
return m_tokens[Current].token;
}
Token Scanner::selectToken(char _next, Token _then, Token _else)
@ -425,10 +427,10 @@ Token Scanner::scanSlash()
{
// doxygen style /// comment
Token comment;
m_nextSkippedComment.location.start = firstSlashPosition;
m_skippedComments[NextNext].location.start = firstSlashPosition;
comment = scanSingleLineDocComment();
m_nextSkippedComment.location.end = sourcePos();
m_nextSkippedComment.token = comment;
m_skippedComments[NextNext].location.end = sourcePos();
m_skippedComments[NextNext].token = comment;
return Token::Whitespace;
}
else
@ -451,10 +453,10 @@ Token Scanner::scanSlash()
}
// we actually have a multiline documentation comment
Token comment;
m_nextSkippedComment.location.start = firstSlashPosition;
m_skippedComments[NextNext].location.start = firstSlashPosition;
comment = scanMultiLineDocComment();
m_nextSkippedComment.location.end = sourcePos();
m_nextSkippedComment.token = comment;
m_skippedComments[NextNext].location.end = sourcePos();
m_skippedComments[NextNext].token = comment;
if (comment == Token::Illegal)
return Token::Illegal; // error already set
else
@ -471,11 +473,8 @@ Token Scanner::scanSlash()
void Scanner::scanToken()
{
m_nextToken.error = ScannerError::NoError;
m_nextToken.literal.clear();
m_nextToken.extendedTokenInfo = make_tuple(0, 0);
m_nextSkippedComment.literal.clear();
m_nextSkippedComment.extendedTokenInfo = make_tuple(0, 0);
m_tokens[NextNext] = {};
m_skippedComments[NextNext] = {};
Token token;
// M and N are for the purposes of grabbing different type sizes
@ -484,7 +483,7 @@ void Scanner::scanToken()
do
{
// Remember the position of the next token
m_nextToken.location.start = sourcePos();
m_tokens[NextNext].location.start = sourcePos();
switch (m_char)
{
case '"':
@ -679,9 +678,9 @@ void Scanner::scanToken()
// whitespace.
}
while (token == Token::Whitespace);
m_nextToken.location.end = sourcePos();
m_nextToken.token = token;
m_nextToken.extendedTokenInfo = make_tuple(m, n);
m_tokens[NextNext].location.end = sourcePos();
m_tokens[NextNext].token = token;
m_tokens[NextNext].extendedTokenInfo = make_tuple(m, n);
}
bool Scanner::scanEscape()
@ -931,5 +930,7 @@ tuple<Token, unsigned, unsigned> Scanner::scanIdentifierOrKeyword()
while (isIdentifierPart(m_char) || (m_char == '.' && m_supportPeriodInIdentifier))
addLiteralCharAndAdvance();
literal.complete();
return TokenTraits::fromIdentifierOrKeyword(m_nextToken.literal);
return TokenTraits::fromIdentifierOrKeyword(m_tokens[NextNext].literal);
}
} // namespace solidity::langutil

View File

@ -55,13 +55,13 @@
#include <liblangutil/Token.h>
#include <liblangutil/CharStream.h>
#include <liblangutil/SourceLocation.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonData.h>
#include <libsolutil/Common.h>
#include <libsolutil/CommonData.h>
#include <optional>
#include <iosfwd>
namespace langutil
namespace solidity::langutil
{
class AstRawString;
@ -121,32 +121,32 @@ public:
/// @returns the current token
Token currentToken() const
{
return m_currentToken.token;
return m_tokens[Current].token;
}
ElementaryTypeNameToken currentElementaryTypeNameToken() const
{
unsigned firstSize;
unsigned secondSize;
std::tie(firstSize, secondSize) = m_currentToken.extendedTokenInfo;
return ElementaryTypeNameToken(m_currentToken.token, firstSize, secondSize);
std::tie(firstSize, secondSize) = m_tokens[Current].extendedTokenInfo;
return ElementaryTypeNameToken(m_tokens[Current].token, firstSize, secondSize);
}
SourceLocation currentLocation() const { return m_currentToken.location; }
std::string const& currentLiteral() const { return m_currentToken.literal; }
std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_currentToken.extendedTokenInfo; }
SourceLocation currentLocation() const { return m_tokens[Current].location; }
std::string const& currentLiteral() const { return m_tokens[Current].literal; }
std::tuple<unsigned, unsigned> const& currentTokenInfo() const { return m_tokens[Current].extendedTokenInfo; }
/// Retrieves the last error that occurred during lexical analysis.
/// @note If no error occurred, the value is undefined.
ScannerError currentError() const noexcept { return m_currentToken.error; }
ScannerError currentError() const noexcept { return m_tokens[Current].error; }
///@}
///@{
///@name Information about the current comment token
SourceLocation currentCommentLocation() const { return m_skippedComment.location; }
std::string const& currentCommentLiteral() const { return m_skippedComment.literal; }
SourceLocation currentCommentLocation() const { return m_skippedComments[Current].location; }
std::string const& currentCommentLiteral() const { return m_skippedComments[Current].literal; }
/// Called by the parser during FunctionDefinition parsing to clear the current comment
void clearCurrentCommentLiteral() { m_skippedComment.literal.clear(); }
void clearCurrentCommentLiteral() { m_skippedComments[Current].literal.clear(); }
///@}
@ -154,9 +154,11 @@ public:
///@name Information about the next token
/// @returns the next token without advancing input.
Token peekNextToken() const { return m_nextToken.token; }
SourceLocation peekLocation() const { return m_nextToken.location; }
std::string const& peekLiteral() const { return m_nextToken.literal; }
Token peekNextToken() const { return m_tokens[Next].token; }
SourceLocation peekLocation() const { return m_tokens[Next].location; }
std::string const& peekLiteral() const { return m_tokens[Next].literal; }
Token peekNextNextToken() const { return m_tokens[NextNext].token; }
///@}
///@{
@ -176,7 +178,7 @@ public:
private:
inline Token setError(ScannerError _error) noexcept
{
m_nextToken.error = _error;
m_tokens[NextNext].error = _error;
return Token::Illegal;
}
@ -192,8 +194,8 @@ private:
///@{
///@name Literal buffer support
inline void addLiteralChar(char c) { m_nextToken.literal.push_back(c); }
inline void addCommentLiteralChar(char c) { m_nextSkippedComment.literal.push_back(c); }
inline void addLiteralChar(char c) { m_tokens[NextNext].literal.push_back(c); }
inline void addCommentLiteralChar(char c) { m_skippedComments[NextNext].literal.push_back(c); }
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
void addUnicodeAsUTF8(unsigned codepoint);
///@}
@ -252,11 +254,10 @@ private:
bool m_supportPeriodInIdentifier = false;
TokenDesc m_skippedComment; // desc for current skipped comment
TokenDesc m_nextSkippedComment; // desc for next skipped comment
enum TokenIndex { Current, Next, NextNext };
TokenDesc m_currentToken; // desc for current token (as returned by Next())
TokenDesc m_nextToken; // desc for next token (one token look-ahead)
TokenDesc m_skippedComments[3] = {}; // desc for the current, next and nextnext skipped comment
TokenDesc m_tokens[3] = {}; // desc for the current, next and nextnext token
std::shared_ptr<CharStream> m_source;

View File

@ -25,8 +25,8 @@
#include <functional>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
SemVerVersion::SemVerVersion(string const& _versionString)
{

View File

@ -26,10 +26,10 @@
#include <string>
#include <vector>
namespace langutil
namespace solidity::langutil
{
class SemVerError: dev::Exception
class SemVerError: util::Exception
{
};

View File

@ -0,0 +1,51 @@
/*
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/>.
*/
#include <liblangutil/Exceptions.h>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
using namespace solidity;
namespace solidity::langutil
{
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex)
{
// Expected input: "start:length:sourceindex"
enum SrcElem : size_t { Start, Length, Index };
std::vector<std::string> pos;
boost::algorithm::split(pos, _input, boost::is_any_of(":"));
astAssert(
pos.size() == 3 &&
_maxIndex >= static_cast<size_t>(stoi(pos[Index])),
"'src'-field ill-formatted or src-index too high"
);
int start = stoi(pos[Start]);
int end = start + stoi(pos[Length]);
// ASSUMPTION: only the name of source is used from here on, the m_source of the CharStream-Object can be empty
std::shared_ptr<langutil::CharStream> source = std::make_shared<langutil::CharStream>("", _sourceName);
return SourceLocation{start, end, source};
}
}

View File

@ -22,18 +22,17 @@
#pragma once
#include <libdevcore/Assertions.h>
#include <libdevcore/Common.h> // defines noexcept macro for MSVC
#include <libdevcore/Exceptions.h>
#include <libsolutil/Assertions.h>
#include <libsolutil/Exceptions.h>
#include <liblangutil/CharStream.h>
#include <memory>
#include <string>
#include <ostream>
#include <tuple>
namespace langutil
namespace solidity::langutil
{
struct SourceLocationError: virtual dev::Exception {};
struct SourceLocationError: virtual util::Exception {};
/**
* Representation of an interval of source positions.
@ -46,9 +45,28 @@ struct SourceLocation
return source.get() == _other.source.get() && start == _other.start && end == _other.end;
}
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
inline bool operator<(SourceLocation const& _other) const;
inline bool contains(SourceLocation const& _other) const;
inline bool intersects(SourceLocation const& _other) const;
inline bool operator<(SourceLocation const& _other) const
{
if (!source|| !_other.source)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
else
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
}
inline bool contains(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return start <= _other.start && _other.end <= end;
}
inline bool intersects(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return _other.start < end && start < _other.end;
}
bool isEmpty() const { return start == -1 && end == -1; }
@ -86,6 +104,8 @@ struct SourceLocation
std::shared_ptr<CharStream> source;
};
SourceLocation const parseSourceLocation(std::string const& _input, std::string const& _sourceName, size_t _maxIndex = -1);
/// Stream output for Location (used e.g. in boost exceptions).
inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _location)
{
@ -100,26 +120,4 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
return _out;
}
bool SourceLocation::operator<(SourceLocation const& _other) const
{
if (!source|| !_other.source)
return std::make_tuple(int(!!source), start, end) < std::make_tuple(int(!!_other.source), _other.start, _other.end);
else
return std::make_tuple(source->name(), start, end) < std::make_tuple(_other.source->name(), _other.start, _other.end);
}
bool SourceLocation::contains(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return start <= _other.start && _other.end <= end;
}
bool SourceLocation::intersects(SourceLocation const& _other) const
{
if (isEmpty() || _other.isEmpty() || source.get() != _other.source.get())
return false;
return _other.start < end && start < _other.end;
}
}

View File

@ -22,14 +22,14 @@
#include <iomanip>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(Exception const& _exception, string _category)
SourceReferenceExtractor::Message SourceReferenceExtractor::extract(util::Exception const& _exception, string _category)
{
SourceLocation const* location = boost::get_error_info<errinfo_sourceLocation>(_exception);
string const* message = boost::get_error_info<errinfo_comment>(_exception);
string const* message = boost::get_error_info<util::errinfo_comment>(_exception);
SourceReference primary = extract(location, message ? *message : "");
std::vector<SourceReference> secondary;
@ -46,6 +46,9 @@ SourceReference SourceReferenceExtractor::extract(SourceLocation const* _locatio
if (!_location || !_location->source.get()) // Nothing we can extract here
return SourceReference::MessageOnly(std::move(message));
if (_location->source->source().empty()) // No source text, so we can only extract the source name
return SourceReference::MessageOnly(std::move(message), _location->source->name());
shared_ptr<CharStream> const& source = _location->source;
LineColumn const interest = source->translatePositionToLineColumn(_location->start);

View File

@ -21,12 +21,12 @@
#include <tuple>
#include <vector>
namespace dev
namespace solidity::util
{
struct Exception;
}
namespace langutil
namespace solidity::langutil
{
struct LineColumn
@ -49,10 +49,11 @@ struct SourceReference
int endColumn = {-1}; ///< Highlighting range-end of text field.
/// Constructs a SourceReference containing a message only.
static SourceReference MessageOnly(std::string _msg)
static SourceReference MessageOnly(std::string _msg, std::string _sourceName = {})
{
SourceReference sref;
sref.message = std::move(_msg);
sref.sourceName = std::move(_sourceName);
return sref;
}
};
@ -68,7 +69,7 @@ namespace SourceReferenceExtractor
std::vector<SourceReference> secondary;
};
Message extract(dev::Exception const& _exception, std::string _category);
Message extract(util::Exception const& _exception, std::string _category);
SourceReference extract(SourceLocation const* _location, std::string message = "");
}

View File

@ -25,8 +25,9 @@
#include <liblangutil/Exceptions.h>
using namespace std;
using namespace dev;
using namespace langutil;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::langutil;
void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _location)
{
@ -70,7 +71,7 @@ void SourceReferenceFormatter::printSourceName(SourceReference const& _ref)
m_stream << _ref.sourceName << ":" << (_ref.position.line + 1) << ":" << (_ref.position.column + 1) << ": ";
}
void SourceReferenceFormatter::printExceptionInformation(dev::Exception const& _exception, std::string const& _category)
void SourceReferenceFormatter::printExceptionInformation(util::Exception const& _exception, std::string const& _category)
{
printExceptionInformation(SourceReferenceExtractor::extract(_exception, _category));
}

View File

@ -28,12 +28,12 @@
#include <liblangutil/Exceptions.h>
#include <liblangutil/SourceReferenceExtractor.h>
namespace dev
namespace solidity::util
{
struct Exception; // forward
}
namespace langutil
namespace solidity::langutil
{
struct SourceLocation;
class Scanner;
@ -52,7 +52,7 @@ public:
virtual void printExceptionInformation(SourceReferenceExtractor::Message const& _msg);
virtual void printSourceLocation(SourceLocation const* _location);
virtual void printExceptionInformation(dev::Exception const& _exception, std::string const& _category);
virtual void printExceptionInformation(util::Exception const& _exception, std::string const& _category);
virtual void printErrorInformation(Error const& _error);
static std::string formatErrorInformation(Error const& _error)
@ -64,7 +64,7 @@ public:
}
static std::string formatExceptionInformation(
dev::Exception const& _exception,
util::Exception const& _exception,
std::string const& _name
)
{

View File

@ -25,9 +25,10 @@
#include <iomanip>
using namespace std;
using namespace dev;
using namespace dev::formatting;
using namespace langutil;
using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::util;
using namespace solidity::util::formatting;
AnsiColorized SourceReferenceFormatterHuman::normalColored() const
{

View File

@ -23,18 +23,18 @@
#include <liblangutil/SourceReferenceExtractor.h>
#include <liblangutil/SourceReferenceFormatter.h> // SourceReferenceFormatterBase
#include <libdevcore/AnsiColorized.h>
#include <libsolutil/AnsiColorized.h>
#include <ostream>
#include <sstream>
#include <functional>
namespace dev
namespace solidity::util
{
struct Exception; // forward
}
namespace langutil
namespace solidity::langutil
{
struct SourceLocation;
@ -52,7 +52,7 @@ public:
using SourceReferenceFormatter::printExceptionInformation;
static std::string formatExceptionInformation(
dev::Exception const& _exception,
util::Exception const& _exception,
std::string const& _name,
bool colored = false
)
@ -65,13 +65,13 @@ public:
}
private:
dev::AnsiColorized normalColored() const;
dev::AnsiColorized frameColored() const;
dev::AnsiColorized errorColored() const;
dev::AnsiColorized messageColored() const;
dev::AnsiColorized secondaryColored() const;
dev::AnsiColorized highlightColored() const;
dev::AnsiColorized diagColored() const;
util::AnsiColorized normalColored() const;
util::AnsiColorized frameColored() const;
util::AnsiColorized errorColored() const;
util::AnsiColorized messageColored() const;
util::AnsiColorized secondaryColored() const;
util::AnsiColorized highlightColored() const;
util::AnsiColorized diagColored() const;
private:
bool m_colored;

View File

@ -46,7 +46,7 @@
using namespace std;
namespace langutil
namespace solidity::langutil
{
void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _first, unsigned const& _second)
@ -82,7 +82,6 @@ void ElementaryTypeNameToken::assertDetails(Token _baseType, unsigned const& _fi
namespace TokenTraits
{
char const* toString(Token tok)
{
switch (tok)

View File

@ -42,7 +42,7 @@
#pragma once
#include <libdevcore/Common.h>
#include <libsolutil/Common.h>
#include <liblangutil/Exceptions.h>
#include <liblangutil/UndefMacros.h>
@ -50,7 +50,7 @@
#include <string>
#include <tuple>
namespace langutil
namespace solidity::langutil
{
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the

View File

@ -1,14 +0,0 @@
set(sources
CodeFragment.cpp
CodeFragment.h
Compiler.cpp
Compiler.h
CompilerState.cpp
CompilerState.h
Exceptions.h
Parser.cpp
Parser.h
)
add_library(lll ${sources})
target_link_libraries(lll PUBLIC evmasm devcore)

View File

@ -1,757 +0,0 @@
/*
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 CodeFragment.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <liblll/CodeFragment.h>
#include <liblll/CompilerState.h>
#include <liblll/Parser.h>
#include <libevmasm/Instruction.h>
#include <libdevcore/CommonIO.h>
#include <boost/algorithm/string.hpp>
#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif // defined(__GNUC__)
#include <boost/spirit/include/support_utree.hpp>
#if defined(__GNUC__)
#pragma GCC diagnostic pop
#endif // defined(__GNUC__)
using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::lll;
void CodeFragment::finalise(CompilerState const& _cs)
{
// NOTE: add this as a safeguard in case the user didn't issue an
// explicit stop at the end of the sequence
m_asm.append(Instruction::STOP);
if (_cs.usedAlloc && _cs.vars.size() && !m_finalised)
{
m_finalised = true;
m_asm.injectStart(Instruction::MSTORE8);
m_asm.injectStart((u256)((_cs.vars.size() + 2) * 32) - 1);
m_asm.injectStart((u256)1);
}
}
namespace
{
/// Returns true iff the instruction is valid in "inline assembly".
bool validAssemblyInstruction(string us)
{
auto it = c_instructions.find(us);
return !(
it == c_instructions.end() ||
isPushInstruction(it->second)
);
}
/// Returns true iff the instruction is valid as a function.
bool validFunctionalInstruction(string us)
{
auto it = c_instructions.find(us);
return !(
it == c_instructions.end() ||
isPushInstruction(it->second) ||
isDupInstruction(it->second) ||
isSwapInstruction(it->second) ||
it->second == Instruction::JUMPDEST
);
}
}
CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM):
m_readFile(_readFile)
{
/*
std::cout << "CodeFragment. Locals:";
for (auto const& i: _s.defs)
std::cout << i.first << ":" << i.second.m_asm.out();
std::cout << "Args:";
for (auto const& i: _s.args)
std::cout << i.first << ":" << i.second.m_asm.out();
std::cout << "Outers:";
for (auto const& i: _s.outers)
std::cout << i.first << ":" << i.second.m_asm.out();
debugOutAST(std::cout, _t);
std::cout << endl << flush;
*/
switch (_t.which())
{
case sp::utree_type::list_type:
constructOperation(_t, _s);
break;
case sp::utree_type::string_type:
{
auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
string s(sr.begin(), sr.end());
m_asm.append(s);
break;
}
case sp::utree_type::symbol_type:
{
auto sr = _t.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
string s(sr.begin(), sr.end());
string us = boost::algorithm::to_upper_copy(s);
if (_allowASM && c_instructions.count(us) && validAssemblyInstruction(us))
m_asm.append(c_instructions.at(us));
else if (_s.defs.count(s))
m_asm.append(_s.defs.at(s).m_asm);
else if (_s.args.count(s))
m_asm.append(_s.args.at(s).m_asm);
else if (_s.outers.count(s))
m_asm.append(_s.outers.at(s).m_asm);
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
{
auto it = _s.vars.find(s);
if (it == _s.vars.end())
error<InvalidName>(std::string("Symbol not found: ") + s);
m_asm.append((u256)it->second.first);
}
else
error<BareSymbol>(s);
break;
}
case sp::utree_type::any_type:
{
bigint i = *_t.get<bigint*>();
if (i < 0 || i > bigint(u256(0) - 1))
error<IntegerOutOfRange>(toString(i));
m_asm.append((u256)i);
break;
}
default:
error<CompilerException>("Unexpected fragment type");
break;
}
}
void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
{
if (_t.tag() == 0 && _t.empty())
error<EmptyList>();
else if (_t.tag() == 0 && _t.front().which() != sp::utree_type::symbol_type)
error<DataNotExecutable>();
else
{
string s;
string us;
switch (_t.tag())
{
case 0:
{
auto sr = _t.front().get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
s = string(sr.begin(), sr.end());
us = boost::algorithm::to_upper_copy(s);
break;
}
case 1:
us = "MLOAD";
break;
case 2:
us = "SLOAD";
break;
case 3:
us = "MSTORE";
break;
case 4:
us = "SSTORE";
break;
case 5:
us = "SEQ";
break;
case 6:
us = "CALLDATALOAD";
break;
default:;
}
auto firstAsString = [&]()
{
auto i = *++_t.begin();
if (i.tag())
error<InvalidName>(toString(i));
if (i.which() == sp::utree_type::string_type)
{
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
return string(sr.begin(), sr.end());
}
else if (i.which() == sp::utree_type::symbol_type)
{
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
return _s.getDef(string(sr.begin(), sr.end())).m_asm.backString();
}
return string();
};
auto varAddress = [&](string const& n, bool createMissing = false)
{
if (n.empty())
error<InvalidName>("Empty variable name not allowed");
auto it = _s.vars.find(n);
if (it == _s.vars.end())
{
if (createMissing)
{
// Create new variable
bool ok;
tie(it, ok) = _s.vars.insert(make_pair(n, make_pair(_s.stackSize, 32)));
_s.stackSize += 32;
}
else
error<InvalidName>(std::string("Symbol not found: ") + n);
}
return it->second.first;
};
// Operations who args are not standard stack-pushers.
bool nonStandard = true;
if (us == "ASM")
{
int c = 0;
for (auto const& i: _t)
if (c++)
{
auto fragment = CodeFragment(i, _s, m_readFile, true).m_asm;
if ((m_asm.deposit() + fragment.deposit()) < 0)
error<IncorrectParameterCount>("The assembly instruction resulted in stack underflow");
m_asm.append(fragment);
}
}
else if (us == "INCLUDE")
{
if (_t.size() != 2)
error<IncorrectParameterCount>(us);
string fileName = firstAsString();
if (fileName.empty())
error<InvalidName>("Empty file name provided");
if (!m_readFile)
error<InvalidName>("Import callback not present");
string contents = m_readFile(fileName);
if (contents.empty())
error<InvalidName>(std::string("File not found (or empty): ") + fileName);
m_asm.append(CodeFragment::compile(std::move(contents), _s, m_readFile).m_asm);
}
else if (us == "SET")
{
// TODO: move this to be a stack variable (and not a memory variable)
if (_t.size() != 3)
error<IncorrectParameterCount>(us);
int c = 0;
for (auto const& i: _t)
if (c++ == 2)
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
m_asm.append((u256)varAddress(firstAsString(), true));
m_asm.append(Instruction::MSTORE);
}
else if (us == "UNSET")
{
// TODO: this doesn't actually free up anything, since it is a memory variable (see "SET")
if (_t.size() != 2)
error<IncorrectParameterCount>();
auto it = _s.vars.find(firstAsString());
if (it != _s.vars.end())
_s.vars.erase(it);
}
else if (us == "GET")
{
if (_t.size() != 2)
error<IncorrectParameterCount>(us);
m_asm.append((u256)varAddress(firstAsString()));
m_asm.append(Instruction::MLOAD);
}
else if (us == "WITH")
{
if (_t.size() != 4)
error<IncorrectParameterCount>();
string key = firstAsString();
if (_s.vars.find(key) != _s.vars.end())
error<InvalidName>(string("Symbol already used: ") + key);
// Create variable
// TODO: move this to be a stack variable (and not a memory variable)
size_t c = 0;
for (auto const& i: _t)
if (c++ == 2)
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
m_asm.append((u256)varAddress(key, true));
m_asm.append(Instruction::MSTORE);
// Insert sub with variable access, but new state
CompilerState ns = _s;
c = 0;
for (auto const& i: _t)
if (c++ == 3)
m_asm.append(CodeFragment(i, _s, m_readFile, false).m_asm);
// Remove variable
auto it = _s.vars.find(key);
if (it != _s.vars.end())
_s.vars.erase(it);
}
else if (us == "REF")
m_asm.append((u256)varAddress(firstAsString()));
else if (us == "DEF")
{
string n;
unsigned ii = 0;
if (_t.size() != 3 && _t.size() != 4)
error<IncorrectParameterCount>(us);
vector<string> args;
for (auto const& i: _t)
{
if (ii == 1)
{
if (i.tag())
error<InvalidName>(toString(i));
if (i.which() == sp::utree_type::string_type)
{
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
n = string(sr.begin(), sr.end());
}
else if (i.which() == sp::utree_type::symbol_type)
{
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
n = _s.getDef(string(sr.begin(), sr.end())).m_asm.backString();
}
}
else if (ii == 2)
if (_t.size() == 3)
{
/// NOTE: some compilers could do the assignment first if this is done in a single line
CodeFragment code = CodeFragment(i, _s, m_readFile);
_s.defs[n] = code;
}
else
for (auto const& j: i)
{
if (j.tag() || j.which() != sp::utree_type::symbol_type)
error<InvalidMacroArgs>();
auto sr = j.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>();
args.emplace_back(sr.begin(), sr.end());
}
else if (ii == 3)
{
auto k = make_pair(n, args.size());
_s.macros[k].code = i;
_s.macros[k].env = _s.outers;
_s.macros[k].args = args;
for (auto const& i: _s.args)
_s.macros[k].env[i.first] = i.second;
for (auto const& i: _s.defs)
_s.macros[k].env[i.first] = i.second;
}
++ii;
}
}
else if (us == "LIT")
{
if (_t.size() < 3)
error<IncorrectParameterCount>(us);
unsigned ii = 0;
CodeFragment pos;
bytes data;
for (auto const& i: _t)
{
if (ii == 0)
{
ii++;
continue;
}
else if (ii == 1)
{
pos = CodeFragment(i, _s, m_readFile);
if (pos.m_asm.deposit() != 1)
error<InvalidDeposit>(toString(i));
}
else if (i.tag() != 0)
{
error<InvalidLiteral>(toString(i));
}
else if (i.which() == sp::utree_type::string_type)
{
auto sr = i.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>();
data.insert(data.end(), (uint8_t const *)sr.begin(), (uint8_t const*)sr.end());
}
else if (i.which() == sp::utree_type::any_type)
{
bigint bi = *i.get<bigint*>();
if (bi < 0)
error<IntegerOutOfRange>(toString(i));
else
{
bytes tmp = toCompactBigEndian(bi);
data.insert(data.end(), tmp.begin(), tmp.end());
}
}
else
{
error<InvalidLiteral>(toString(i));
}
ii++;
}
m_asm.append((u256)data.size());
m_asm.append(Instruction::DUP1);
m_asm.append(data);
m_asm.append(pos.m_asm, 1);
m_asm.append(Instruction::CODECOPY);
}
else
nonStandard = false;
if (nonStandard)
return;
std::map<std::string, Instruction> const c_arith = {
{ "+", Instruction::ADD },
{ "-", Instruction::SUB },
{ "*", Instruction::MUL },
{ "/", Instruction::DIV },
{ "%", Instruction::MOD },
{ "&", Instruction::AND },
{ "|", Instruction::OR },
{ "^", Instruction::XOR }
};
std::map<std::string, pair<Instruction, bool>> const c_binary = {
{ "<", { Instruction::LT, false } },
{ "<=", { Instruction::GT, true } },
{ ">", { Instruction::GT, false } },
{ ">=", { Instruction::LT, true } },
{ "S<", { Instruction::SLT, false } },
{ "S<=", { Instruction::SGT, true } },
{ "S>", { Instruction::SGT, false } },
{ "S>=", { Instruction::SLT, true } },
{ "=", { Instruction::EQ, false } },
{ "!=", { Instruction::EQ, true } }
};
std::map<std::string, Instruction> const c_unary = {
{ "!", Instruction::ISZERO },
{ "~", Instruction::NOT }
};
vector<CodeFragment> code;
CompilerState ns = _s;
ns.vars.clear();
ns.usedAlloc = false;
int c = _t.tag() ? 1 : 0;
for (auto const& i: _t)
if (c++)
{
if (us == "LLL" && c == 1)
code.emplace_back(i, ns, m_readFile);
else
code.emplace_back(i, _s, m_readFile);
}
auto requireSize = [&](unsigned s) { if (code.size() != s) error<IncorrectParameterCount>(us); };
auto requireMinSize = [&](unsigned s) { if (code.size() < s) error<IncorrectParameterCount>(us); };
auto requireMaxSize = [&](unsigned s) { if (code.size() > s) error<IncorrectParameterCount>(us); };
auto requireDeposit = [&](unsigned i, int s) { if (code[i].m_asm.deposit() != s) error<InvalidDeposit>(us); };
if (_s.macros.count(make_pair(s, code.size())))
{
Macro const& m = _s.macros.at(make_pair(s, code.size()));
CompilerState cs = _s;
for (auto const& i: m.env)
cs.outers[i.first] = i.second;
for (auto const& i: cs.defs)
cs.outers[i.first] = i.second;
cs.defs.clear();
for (unsigned i = 0; i < m.args.size(); ++i)
{
//requireDeposit(i, 1);
cs.args[m.args[i]] = code[i];
}
m_asm.append(CodeFragment(m.code, cs, m_readFile).m_asm);
for (auto const& i: cs.defs)
_s.defs[i.first] = i.second;
for (auto const& i: cs.macros)
_s.macros.insert(i);
}
else if (c_instructions.count(us) && validFunctionalInstruction(us))
{
auto it = c_instructions.find(us);
requireSize(instructionInfo(it->second).args);
for (unsigned i = code.size(); i; --i)
m_asm.append(code[i - 1].m_asm, 1);
m_asm.append(it->second);
}
else if (c_arith.count(us))
{
auto it = c_arith.find(us);
requireMinSize(1);
for (unsigned i = code.size(); i; --i)
{
requireDeposit(i - 1, 1);
m_asm.append(code[i - 1].m_asm, 1);
}
for (unsigned i = 1; i < code.size(); ++i)
m_asm.append(it->second);
}
else if (c_binary.count(us))
{
auto it = c_binary.find(us);
requireSize(2);
requireDeposit(0, 1);
requireDeposit(1, 1);
m_asm.append(code[1].m_asm, 1);
m_asm.append(code[0].m_asm, 1);
m_asm.append(it->second.first);
if (it->second.second)
m_asm.append(Instruction::ISZERO);
}
else if (c_unary.count(us))
{
auto it = c_unary.find(us);
requireSize(1);
requireDeposit(0, 1);
m_asm.append(code[0].m_asm, 1);
m_asm.append(it->second);
}
else if (us == "IF")
{
requireSize(3);
requireDeposit(0, 1);
int minDep = min(code[1].m_asm.deposit(), code[2].m_asm.deposit());
m_asm.append(code[0].m_asm);
auto mainBranch = m_asm.appendJumpI();
/// The else branch.
int startDeposit = m_asm.deposit();
m_asm.append(code[2].m_asm, minDep);
auto end = m_asm.appendJump();
int deposit = m_asm.deposit();
m_asm.setDeposit(startDeposit);
/// The main branch.
m_asm << mainBranch.tag();
m_asm.append(code[1].m_asm, minDep);
m_asm << end.tag();
if (m_asm.deposit() != deposit)
error<InvalidDeposit>(us);
}
else if (us == "WHEN" || us == "UNLESS")
{
requireSize(2);
requireDeposit(0, 1);
m_asm.append(code[0].m_asm);
if (us == "WHEN")
m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI();
m_asm.append(code[1].m_asm, 0);
m_asm << end.tag();
}
else if (us == "WHILE" || us == "UNTIL")
{
requireSize(2);
requireDeposit(0, 1);
auto begin = m_asm.append(m_asm.newTag());
m_asm.append(code[0].m_asm);
if (us == "WHILE")
m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI();
m_asm.append(code[1].m_asm, 0);
m_asm.appendJump(begin);
m_asm << end.tag();
}
else if (us == "FOR")
{
requireSize(4);
requireDeposit(1, 1);
m_asm.append(code[0].m_asm, 0);
auto begin = m_asm.append(m_asm.newTag());
m_asm.append(code[1].m_asm);
m_asm.append(Instruction::ISZERO);
auto end = m_asm.appendJumpI();
m_asm.append(code[3].m_asm, 0);
m_asm.append(code[2].m_asm, 0);
m_asm.appendJump(begin);
m_asm << end.tag();
}
else if (us == "SWITCH")
{
requireMinSize(1);
bool hasDefault = (code.size() % 2 == 1);
int startDeposit = m_asm.deposit();
int targetDeposit = hasDefault ? code[code.size() - 1].m_asm.deposit() : 0;
// The conditions
eth::AssemblyItems jumpTags;
for (unsigned i = 0; i < code.size() - 1; i += 2)
{
requireDeposit(i, 1);
m_asm.append(code[i].m_asm);
jumpTags.push_back(m_asm.appendJumpI());
}
// The default, if present
if (hasDefault)
m_asm.append(code[code.size() - 1].m_asm);
// The targets - appending in reverse makes the top case the most efficient.
if (code.size() > 1)
{
auto end = m_asm.appendJump();
for (int i = 2 * (code.size() / 2 - 1); i >= 0; i -= 2)
{
m_asm << jumpTags[i / 2].tag();
requireDeposit(i + 1, targetDeposit);
m_asm.append(code[i + 1].m_asm);
if (i != 0)
m_asm.appendJump(end);
}
m_asm << end.tag();
}
m_asm.setDeposit(startDeposit + targetDeposit);
}
else if (us == "ALLOC")
{
requireSize(1);
requireDeposit(0, 1);
// (alloc N):
// - Evaluates to (msize) before the allocation - the start of the allocated memory
// - Does not allocate memory when N is zero
// - Size of memory allocated is N bytes rounded up to a multiple of 32
// - Uses MLOAD to expand MSIZE to avoid modifying memory.
auto end = m_asm.newTag();
m_asm.append(Instruction::MSIZE); // Result will be original top of memory
m_asm.append(code[0].m_asm, 1); // The alloc argument N
m_asm.append(Instruction::DUP1);
m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE
m_asm.appendJumpI(end);
m_asm.append(u256(1));
m_asm.append(Instruction::DUP2); // Copy N
m_asm.append(Instruction::SUB); // N-1
m_asm.append(u256(0x1f)); // Bit mask
m_asm.append(Instruction::NOT); // Invert
m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary
m_asm.append(Instruction::MSIZE); // MSIZE is cheap
m_asm.append(Instruction::ADD);
m_asm.append(Instruction::MLOAD); // Updates MSIZE
m_asm.append(Instruction::POP); // Discard the result of the MLOAD
m_asm.append(end);
m_asm.append(Instruction::POP); // Discard duplicate N
_s.usedAlloc = true;
}
else if (us == "LLL")
{
requireMinSize(2);
requireMaxSize(3);
requireDeposit(1, 1);
auto subPush = m_asm.appendSubroutine(make_shared<eth::Assembly>(code[0].assembly(ns)));
m_asm.append(Instruction::DUP1);
if (code.size() == 3)
{
requireDeposit(2, 1);
m_asm.append(code[2].m_asm, 1);
m_asm.append(Instruction::LT);
m_asm.append(Instruction::ISZERO);
m_asm.append(Instruction::MUL);
m_asm.append(Instruction::DUP1);
}
m_asm.append(subPush);
m_asm.append(code[1].m_asm, 1);
m_asm.append(Instruction::CODECOPY);
}
else if (us == "&&" || us == "||")
{
requireMinSize(1);
for (unsigned i = 0; i < code.size(); ++i)
requireDeposit(i, 1);
auto end = m_asm.newTag();
if (code.size() > 1)
{
m_asm.append((u256)(us == "||" ? 1 : 0));
for (unsigned i = 1; i < code.size(); ++i)
{
// Check if true - predicate
m_asm.append(code[i - 1].m_asm, 1);
if (us == "&&")
m_asm.append(Instruction::ISZERO);
m_asm.appendJumpI(end);
}
m_asm.append(Instruction::POP);
}
// Check if true - predicate
m_asm.append(code.back().m_asm, 1);
// At end now.
m_asm.append(end);
}
else if (us == "SEQ")
{
unsigned ii = 0;
for (auto const& i: code)
if (++ii < code.size())
m_asm.append(i.m_asm, 0);
else
m_asm.append(i.m_asm);
}
else if (us == "RAW")
{
for (auto const& i: code)
m_asm.append(i.m_asm);
// Leave only the last item on stack.
while (m_asm.deposit() > 1)
m_asm.append(Instruction::POP);
}
else if (us == "BYTECODESIZE")
{
m_asm.appendProgramSize();
}
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
m_asm.append((u256)varAddress(s));
else
error<InvalidOperation>("Unsupported keyword: '" + us + "'");
}
}
CodeFragment CodeFragment::compile(string _src, CompilerState& _s, ReadCallback const& _readFile)
{
CodeFragment ret;
sp::utree o;
parseTreeLLL(std::move(_src), o);
if (!o.empty())
ret = CodeFragment(o, _s, _readFile);
_s.treesToKill.push_back(o);
return ret;
}

View File

@ -1,71 +0,0 @@
/*
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 CodeFragment.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <liblll/Exceptions.h>
#include <libevmasm/Instruction.h>
#include <libevmasm/Assembly.h>
#include <libdevcore/Common.h>
namespace boost { namespace spirit { class utree; } }
namespace sp = boost::spirit;
namespace dev
{
namespace lll
{
struct CompilerState;
class CodeFragment
{
public:
using ReadCallback = std::function<std::string(std::string const&)>;
CodeFragment() = default;
CodeFragment(sp::utree const& _t, CompilerState& _s, ReadCallback const& _readFile, bool _allowASM = false);
static CodeFragment compile(std::string _src, CompilerState& _s, ReadCallback const& _readFile);
/// Consolidates data and compiles code.
eth::Assembly& assembly(CompilerState const& _cs) { finalise(_cs); return m_asm; }
private:
void finalise(CompilerState const& _cs);
template <class T> static void error() { BOOST_THROW_EXCEPTION(T() ); }
template <class T> static void error(std::string const& reason) {
auto err = T();
err << errinfo_comment(reason);
BOOST_THROW_EXCEPTION(err);
}
void constructOperation(sp::utree const& _t, CompilerState& _s);
bool m_finalised = false;
eth::Assembly m_asm;
ReadCallback m_readFile;
};
static CodeFragment const NullCodeFragment;
}
}

View File

@ -1,125 +0,0 @@
/*
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 Compiler.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <liblll/Compiler.h>
#include <liblll/Parser.h>
#include <liblll/CompilerState.h>
#include <liblll/CodeFragment.h>
using namespace std;
using namespace dev;
using namespace dev::lll;
bytes dev::lll::compileLLL(string _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
{
try
{
CompilerState cs;
cs.populateStandard();
auto assembly = CodeFragment::compile(std::move(_src), cs, _readFile).assembly(cs);
if (_opt)
assembly = assembly.optimise(true, _evmVersion, true, 200);
bytes ret = assembly.assemble().bytecode;
for (auto i: cs.treesToKill)
killBigints(i);
return ret;
}
catch (Exception const& _e)
{
if (_errors)
{
_errors->emplace_back("Parse error.");
_errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (std::exception const& _e)
{
if (_errors)
{
_errors->emplace_back("Parse exception.");
_errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (...)
{
if (_errors)
_errors->emplace_back("Internal compiler exception.");
}
return bytes();
}
std::string dev::lll::compileLLLToAsm(std::string _src, langutil::EVMVersion _evmVersion, bool _opt, std::vector<std::string>* _errors, ReadCallback const& _readFile)
{
try
{
CompilerState cs;
cs.populateStandard();
auto assembly = CodeFragment::compile(std::move(_src), cs, _readFile).assembly(cs);
if (_opt)
assembly = assembly.optimise(true, _evmVersion, true, 200);
string ret = assembly.assemblyString();
for (auto i: cs.treesToKill)
killBigints(i);
return ret;
}
catch (Exception const& _e)
{
if (_errors)
{
_errors->emplace_back("Parse error.");
_errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (std::exception const& _e)
{
if (_errors)
{
_errors->emplace_back("Parse exception.");
_errors->emplace_back(boost::diagnostic_information(_e));
}
}
catch (...)
{
if (_errors)
_errors->emplace_back("Internal compiler exception.");
}
return string();
}
string dev::lll::parseLLL(string _src)
{
sp::utree o;
try
{
parseTreeLLL(std::move(_src), o);
}
catch (...)
{
killBigints(o);
return string();
}
ostringstream ret;
debugOutAST(ret, o);
killBigints(o);
return ret.str();
}

View File

@ -1,43 +0,0 @@
/*
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 Compiler.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Common.h>
#include <liblangutil/EVMVersion.h>
#include <string>
#include <vector>
namespace dev
{
namespace lll
{
using ReadCallback = std::function<std::string(std::string const&)>;
std::string parseLLL(std::string _src);
std::string compileLLLToAsm(std::string _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
bytes compileLLL(std::string _src, langutil::EVMVersion _evmVersion, bool _opt = true, std::vector<std::string>* _errors = nullptr, ReadCallback const& _readFile = ReadCallback());
}
}

View File

@ -1,86 +0,0 @@
/*
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 CompilerState.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <liblll/CompilerState.h>
#include <liblll/CodeFragment.h>
using namespace std;
using namespace dev;
using namespace dev::lll;
CompilerState::CompilerState()
{
}
CodeFragment const& CompilerState::getDef(std::string const& _s) const
{
if (defs.count(_s))
return defs.at(_s);
else if (args.count(_s))
return args.at(_s);
else if (outers.count(_s))
return outers.at(_s);
else
return NullCodeFragment;
}
void CompilerState::populateStandard()
{
static string const s = "{"
"(def 'panic () (asm INVALID))"
// Alternative macro version of alloc, which is currently implemented in the parser
// "(def 'alloc (n) (raw (msize) (when n (pop (mload (+ (msize) (& (- n 1) (~ 0x1f))))))))"
"(def 'allgas (- (gas) 21))"
"(def 'send (to value) (call allgas to value 0 0 0 0))"
"(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))"
// NOTE: in this macro, memory location 0 is set in order to force msize to be at least 32 bytes.
"(def 'msg (gaslimit to value data datasize outsize) { [0]:0 [0]:(msize) (call gaslimit to value data datasize @0 outsize) @0 })"
"(def 'msg (gaslimit to value data datasize) { (call gaslimit to value data datasize 0 32) @0 })"
"(def 'msg (gaslimit to value data) { [0]:data (msg gaslimit to value 0 32) })"
"(def 'msg (to value data) { [0]:data (msg allgas to value 0 32) })"
"(def 'msg (to data) { [0]:data (msg allgas to 0 0 32) })"
// NOTE: in the create macros, memory location 0 is set in order to force msize to be at least 32 bytes.
"(def 'create (value code) { [0]:0 [0]:(msize) (create value @0 (lll code @0)) })"
"(def 'create (code) { [0]:0 [0]:(msize) (create 0 @0 (lll code @0)) })"
"(def 'sha3 (loc len) (keccak256 loc len))"
"(def 'sha3 (val) { [0]:val (sha3 0 32) })"
"(def 'sha3pair (a b) { [0]:a [32]:b (sha3 0 64) })"
"(def 'sha3trip (a b c) { [0]:a [32]:b [64]:c (sha3 0 96) })"
"(def 'return (val) { [0]:val (return 0 32) })"
"(def 'returnlll (code) (return 0 (lll code 0)) )"
"(def 'makeperm (name pos) { (def name (sload pos)) (def name (v) (sstore pos v)) } )"
"(def 'permcount 0)"
"(def 'perm (name) { (makeperm name permcount) (def 'permcount (+ permcount 1)) } )"
"(def 'ecrecover (hash v r s) { [0] hash [32] v [64] r [96] s (msg allgas 1 0 0 128) })"
"(def 'sha256 (data datasize) (msg allgas 2 0 data datasize))"
"(def 'ripemd160 (data datasize) (msg allgas 3 0 data datasize))"
"(def 'sha256 (val) { [0]:val (sha256 0 32) })"
"(def 'ripemd160 (val) { [0]:val (ripemd160 0 32) })"
"(def 'wei 1)"
"(def 'szabo 1000000000000)"
"(def 'finney 1000000000000000)"
"(def 'ether 1000000000000000000)"
// these could be replaced by native instructions once supported by EVM
"(def 'shl (val shift) (mul val (exp 2 shift)))"
"(def 'shr (val shift) (div val (exp 2 shift)))"
"}";
CodeFragment::compile(s, *this, CodeFragment::ReadCallback());
}

View File

@ -1,57 +0,0 @@
/*
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 CompilerState.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <liblll/CodeFragment.h>
#include <boost/spirit/include/support_utree.hpp>
namespace dev
{
namespace lll
{
struct Macro
{
std::vector<std::string> args;
boost::spirit::utree code;
std::map<std::string, CodeFragment> env;
};
struct CompilerState
{
CompilerState();
CodeFragment const& getDef(std::string const& _s) const;
void populateStandard();
unsigned stackSize = 128;
std::map<std::string, std::pair<unsigned, unsigned>> vars; ///< maps name to stack offset & size.
std::map<std::string, CodeFragment> defs;
std::map<std::string, CodeFragment> args;
std::map<std::string, CodeFragment> outers;
std::map<std::pair<std::string, unsigned>, Macro> macros;
std::vector<boost::spirit::utree> treesToKill;
bool usedAlloc = false;
};
}
}

View File

@ -1,45 +0,0 @@
/*
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 Exceptions.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <libdevcore/Exceptions.h>
namespace dev
{
namespace lll
{
/// Compile a Low-level Lisp-like Language program into EVM-code.
class CompilerException: public dev::Exception {};
class InvalidOperation: public CompilerException {};
class IntegerOutOfRange: public CompilerException {};
class EmptyList: public CompilerException {};
class DataNotExecutable: public CompilerException {};
class IncorrectParameterCount: public CompilerException {};
class InvalidName: public CompilerException {};
class InvalidMacroArgs: public CompilerException {};
class InvalidLiteral: public CompilerException {};
class BareSymbol: public CompilerException {};
class ParserException: public CompilerException {};
}
}

View File

@ -1,155 +0,0 @@
/*
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 Parser.cpp
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#include <liblll/Parser.h>
#if _MSC_VER
#pragma warning(disable:4348)
#endif
#define BOOST_RESULT_OF_USE_DECLTYPE
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_utree.hpp>
using namespace std;
using namespace dev;
using namespace dev::lll;
namespace qi = boost::spirit::qi;
namespace px = boost::phoenix;
namespace sp = boost::spirit;
void dev::lll::killBigints(sp::utree const& _this)
{
switch (_this.which())
{
case sp::utree_type::list_type: for (auto const& i: _this) killBigints(i); break;
case sp::utree_type::any_type: delete _this.get<bigint*>(); break;
default:;
}
}
void dev::lll::debugOutAST(ostream& _out, sp::utree const& _this)
{
switch (_this.which())
{
case sp::utree_type::list_type:
switch (_this.tag())
{
case 0: _out << "( "; for (auto const& i: _this) { debugOutAST(_out, i); _out << " "; } _out << ")"; break;
case 1: _out << "@ "; debugOutAST(_out, _this.front()); break;
case 2: _out << "@@ "; debugOutAST(_out, _this.front()); break;
case 3: _out << "[ "; debugOutAST(_out, _this.front()); _out << " ] "; debugOutAST(_out, _this.back()); break;
case 4: _out << "[[ "; debugOutAST(_out, _this.front()); _out << " ]] "; debugOutAST(_out, _this.back()); break;
case 5: _out << "{ "; for (auto const& i: _this) { debugOutAST(_out, i); _out << " "; } _out << "}"; break;
case 6: _out << "$ "; debugOutAST(_out, _this.front()); break;
default:;
}
break;
case sp::utree_type::int_type: _out << _this.get<int>(); break;
case sp::utree_type::string_type: _out << "\"" << _this.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::string_type>>() << "\""; break;
case sp::utree_type::symbol_type: _out << _this.get<sp::basic_string<boost::iterator_range<char const*>, sp::utree_type::symbol_type>>(); break;
case sp::utree_type::any_type: _out << *_this.get<bigint*>(); break;
default: _out << "nil";
}
}
namespace dev {
namespace lll {
namespace parseTreeLLL_ {
template<unsigned N>
struct tagNode
{
void operator()(sp::utree& n, qi::rule<string::const_iterator, qi::ascii::space_type, sp::utree()>::context_type& c) const
{
(boost::fusion::at_c<0>(c.attributes) = n).tag(N);
}
};
}}}
void dev::lll::parseTreeLLL(string const& _s, sp::utree& o_out)
{
using qi::standard::space;
using qi::standard::space_type;
using dev::lll::parseTreeLLL_::tagNode;
using symbol_type = sp::basic_string<std::string, sp::utree_type::symbol_type>;
using it = string::const_iterator;
qi::rule<it, space_type, sp::utree()> element;
qi::rule<it, string()> str = '"' > qi::lexeme[+(~qi::char_(std::string("\"") + '\0'))] > '"';
qi::rule<it, string()> strsh = '\'' > qi::lexeme[+(~qi::char_(std::string(" ;$@()[]{}:\n\t") + '\0'))];
qi::rule<it, symbol_type()> symbol = qi::lexeme[+(~qi::char_(std::string(" $@[]{}:();\"\x01-\x1f\x7f") + '\0'))];
qi::rule<it, string()> intstr = qi::lexeme[ qi::no_case["0x"][qi::_val = "0x"] >> +qi::char_("0-9a-fA-F")[qi::_val += qi::_1]] | qi::lexeme[+qi::char_("0-9")[qi::_val += qi::_1]];
qi::rule<it, sp::utree()> integer = intstr[qi::_val = px::construct<sp::any_ptr>(px::new_<bigint>(qi::_1))];
qi::rule<it, space_type, sp::utree()> atom = integer[qi::_val = qi::_1] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = qi::_1];
qi::rule<it, space_type, sp::utree::list_type()> seq = '{' > *element > '}';
qi::rule<it, space_type, sp::utree::list_type()> mload = '@' > element;
qi::rule<it, space_type, sp::utree::list_type()> sload = qi::lit("@@") > element;
qi::rule<it, space_type, sp::utree::list_type()> mstore = '[' > element > ']' > -qi::lit(":") > element;
qi::rule<it, space_type, sp::utree::list_type()> sstore = qi::lit("[[") > element > qi::lit("]]") > -qi::lit(":") > element;
qi::rule<it, space_type, sp::utree::list_type()> calldataload = qi::lit("$") > element;
qi::rule<it, space_type, sp::utree::list_type()> list = '(' > *element > ')';
qi::rule<it, space_type, sp::utree()> extra = sload[tagNode<2>()] | mload[tagNode<1>()] | sstore[tagNode<4>()] | mstore[tagNode<3>()] | seq[tagNode<5>()] | calldataload[tagNode<6>()];
element = atom | list | extra;
string s;
s.reserve(_s.size());
bool incomment = false;
bool instring = false;
bool insstring = false;
for (auto i: _s)
{
if (i == ';' && !instring && !insstring)
incomment = true;
else if (i == '\n')
incomment = instring = insstring = false;
else if (i == '"' && !insstring)
instring = !instring;
else if (i == '\'')
insstring = true;
else if (i == ' ')
insstring = false;
if (!incomment)
s.push_back(i);
}
auto ret = s.cbegin();
try
{
qi::phrase_parse(ret, s.cend(), element, space, qi::skip_flag::dont_postskip, o_out);
}
catch (qi::expectation_failure<it> const& e)
{
std::string fragment(e.first, e.last);
std::string loc = to_string(std::distance(s.cbegin(), e.first) - 1);
std::string reason("Lexer failure at " + loc + ": '" + fragment + "'");
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment(reason));
}
for (auto i = ret; i != s.cend(); ++i)
if (!isspace(*i))
{
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser"));
}
}

View File

@ -1,42 +0,0 @@
/*
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 Parser.h
* @author Gav Wood <i@gavwood.com>
* @date 2014
*/
#pragma once
#include <liblll/Exceptions.h>
#include <libdevcore/Common.h>
#include <string>
#include <vector>
namespace boost { namespace spirit { class utree; } }
namespace sp = boost::spirit;
namespace dev
{
namespace lll
{
void killBigints(sp::utree const& _this);
void parseTreeLLL(std::string const& _s, sp::utree& o_out);
void debugOutAST(std::ostream& _out, sp::utree const& _this);
}
}

View File

@ -24,8 +24,8 @@
#include <libsolidity/interface/StandardCompiler.h>
#include <libsolidity/interface/Version.h>
#include <libyul/YulString.h>
#include <libdevcore/Common.h>
#include <libdevcore/JSON.h>
#include <libsolutil/Common.h>
#include <libsolutil/JSON.h>
#include <cstdlib>
#include <list>
@ -34,8 +34,11 @@
#include "license.h"
using namespace std;
using namespace dev;
using namespace solidity;
using namespace solidity::util;
using solidity::frontend::ReadCallback;
using solidity::frontend::StandardCompiler;
namespace
{
@ -122,7 +125,7 @@ extern char const* solidity_license() noexcept
extern char const* solidity_version() noexcept
{
return VersionString.c_str();
return frontend::VersionString.c_str();
}
extern char* solidity_compile(char const* _input, CStyleReadFileCallback _readCallback, void* _readContext) noexcept

View File

@ -39,10 +39,14 @@ set(sources
ast/ASTAnnotations.h
ast/ASTEnums.h
ast/ASTForward.h
ast/AsmJsonImporter.cpp
ast/AsmJsonImporter.h
ast/ASTJsonConverter.cpp
ast/ASTJsonConverter.h
ast/ASTUtils.cpp
ast/ASTUtils.h
ast/ASTJsonImporter.cpp
ast/ASTJsonImporter.h
ast/ASTVisitor.h
ast/ExperimentalFeatures.h
ast/Types.cpp
@ -151,7 +155,7 @@ if (NOT (${Z3_FOUND} OR ${CVC4_FOUND}))
endif()
add_library(solidity ${sources} ${z3_SRCS} ${cvc4_SRCS})
target_link_libraries(solidity PUBLIC yul evmasm langutil devcore Boost::boost Boost::filesystem Boost::system)
target_link_libraries(solidity PUBLIC yul evmasm langutil solutil Boost::boost Boost::filesystem Boost::system)
if (${Z3_FOUND})
target_link_libraries(solidity PUBLIC z3::libz3)

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