mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into release
This commit is contained in:
commit
b318366e6f
@ -135,10 +135,8 @@ git:
|
|||||||
cache:
|
cache:
|
||||||
ccache: true
|
ccache: true
|
||||||
directories:
|
directories:
|
||||||
- cryptopp
|
|
||||||
- boost_1_57_0
|
- boost_1_57_0
|
||||||
- build
|
- build
|
||||||
- jsoncpp
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- test $TRAVIS_INSTALL_DEPS != On || ./scripts/install_deps.sh
|
- test $TRAVIS_INSTALL_DEPS != On || ./scripts/install_deps.sh
|
||||||
@ -164,7 +162,7 @@ script:
|
|||||||
# in Solidity's RPC setup and some will be in 'eth'. It seems unlikely that Solidity
|
# in Solidity's RPC setup and some will be in 'eth'. It seems unlikely that Solidity
|
||||||
# itself is broken from the failure messages which we are seeing.
|
# itself is broken from the failure messages which we are seeing.
|
||||||
#
|
#
|
||||||
# More details on known issues at https://github.com/ethereum/solidity/issues/769
|
# More details on known issues at https://github.com/ethereum/solidity/issues/769
|
||||||
- test $TRAVIS_TESTS != On || (cd $TRAVIS_BUILD_DIR && (./scripts/tests.sh || ./scripts/tests.sh || ./scripts/tests.sh) )
|
- test $TRAVIS_TESTS != On || (cd $TRAVIS_BUILD_DIR && (./scripts/tests.sh || ./scripts/tests.sh || ./scripts/tests.sh) )
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
|
@ -8,11 +8,12 @@ include(EthPolicy)
|
|||||||
eth_policy()
|
eth_policy()
|
||||||
|
|
||||||
# project name and version should be set after cmake_policy CMP0048
|
# project name and version should be set after cmake_policy CMP0048
|
||||||
set(PROJECT_VERSION "0.4.4")
|
set(PROJECT_VERSION "0.4.5")
|
||||||
project(solidity VERSION ${PROJECT_VERSION})
|
project(solidity VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
# Let's find our dependencies
|
# Let's find our dependencies
|
||||||
include(EthDependencies)
|
include(EthDependencies)
|
||||||
|
include(deps/jsoncpp.cmake)
|
||||||
|
|
||||||
# Figure out what compiler and system are we using
|
# Figure out what compiler and system are we using
|
||||||
include(EthCompilerSettings)
|
include(EthCompilerSettings)
|
||||||
|
17
Changelog.md
17
Changelog.md
@ -1,3 +1,20 @@
|
|||||||
|
### 0.4.5 (2016-11-21)
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Function types
|
||||||
|
* Do-while loops: support for a ``do <block> while (<expr>);`` control structure
|
||||||
|
* Inline assembly: support ``invalidJumpLabel`` as a jump label.
|
||||||
|
* Type checker: now more eagerly searches for a common type of an inline array with mixed types
|
||||||
|
* Code generator: generates a runtime error when an out-of-range value is converted into an enum type.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
|
||||||
|
* Inline assembly: calculate stack height warning correctly even when local variables are used.
|
||||||
|
* Code generator: check for value transfer in non-payable constructors.
|
||||||
|
* Parser: disallow empty enum definitions.
|
||||||
|
* Type checker: disallow conversion between different enum types.
|
||||||
|
* Interface JSON: do not include trailing new line.
|
||||||
|
|
||||||
### 0.4.4 (2016-10-31)
|
### 0.4.4 (2016-10-31)
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
|
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- release
|
||||||
- develop
|
- develop
|
||||||
os: Visual Studio 2015
|
os: Visual Studio 2015
|
||||||
configuration:
|
configuration:
|
||||||
@ -83,14 +83,13 @@ artifacts:
|
|||||||
# below.
|
# below.
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
description: 'Development build of solidity at commit $(APPVEYOR_REPO_COMMIT).\n\n$(APPVEYOR_REPO_COMMIT_MESSAGE)\n\nCommitted by $(APPVEYOR_REPO_COMMIT_AUTHOR), $(APPVEYOR_REPO_COMMIT_TIMESTAMP).'
|
|
||||||
prerelease: true
|
|
||||||
provider: GitHub
|
provider: GitHub
|
||||||
auth_token:
|
auth_token:
|
||||||
secure: HPjiugbDSCsEFTphj/qwHuSw80/BV1xWoSvj95CPmtb16Ukh2VQbLVB7iFtZSans
|
secure: HPjiugbDSCsEFTphj/qwHuSw80/BV1xWoSvj95CPmtb16Ukh2VQbLVB7iFtZSans
|
||||||
artifact: solidity-windows-zip
|
artifact: solidity-windows-zip
|
||||||
on:
|
on:
|
||||||
branch: release
|
branch: release
|
||||||
|
appveyor_repo_tag: true
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
- provider: GitHubPullRequest
|
- provider: GitHubPullRequest
|
||||||
|
@ -71,7 +71,7 @@ if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MA
|
|||||||
add_compile_options(-fPIC)
|
add_compile_options(-fPIC)
|
||||||
|
|
||||||
# Configuration-specific compiler settings.
|
# Configuration-specific compiler settings.
|
||||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DETH_DEBUG")
|
set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DETH_DEBUG")
|
||||||
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
|
set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG")
|
||||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
|
||||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
|
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -g")
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
# Find jsoncpp
|
|
||||||
#
|
|
||||||
# Find the jsoncpp includes and library
|
|
||||||
#
|
|
||||||
# if you nee to add a custom library search path, do it via via CMAKE_PREFIX_PATH
|
|
||||||
#
|
|
||||||
# This module defines
|
|
||||||
# JSONCPP_INCLUDE_DIRS, where to find header, etc.
|
|
||||||
# JSONCPP_LIBRARIES, the libraries needed to use jsoncpp.
|
|
||||||
# JSONCPP_FOUND, If false, do not try to use jsoncpp.
|
|
||||||
|
|
||||||
# only look in default directories
|
|
||||||
find_path(
|
|
||||||
JSONCPP_INCLUDE_DIR
|
|
||||||
NAMES json/json.h
|
|
||||||
PATH_SUFFIXES jsoncpp
|
|
||||||
DOC "jsoncpp include dir"
|
|
||||||
)
|
|
||||||
|
|
||||||
find_library(
|
|
||||||
JSONCPP_LIBRARY
|
|
||||||
NAMES jsoncpp
|
|
||||||
DOC "jsoncpp library"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(JSONCPP_INCLUDE_DIRS ${JSONCPP_INCLUDE_DIR})
|
|
||||||
set(JSONCPP_LIBRARIES ${JSONCPP_LIBRARY})
|
|
||||||
|
|
||||||
# debug library on windows
|
|
||||||
# same naming convention as in qt (appending debug library with d)
|
|
||||||
# boost is using the same "hack" as us with "optimized" and "debug"
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
|
||||||
|
|
||||||
find_library(
|
|
||||||
JSONCPP_LIBRARY_DEBUG
|
|
||||||
NAMES jsoncppd
|
|
||||||
DOC "jsoncpp debug library"
|
|
||||||
)
|
|
||||||
|
|
||||||
set(JSONCPP_LIBRARIES optimized ${JSONCPP_LIBRARIES} debug ${JSONCPP_LIBRARY_DEBUG})
|
|
||||||
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# handle the QUIETLY and REQUIRED arguments and set JSONCPP_FOUND to TRUE
|
|
||||||
# if all listed variables are TRUE, hide their existence from configuration view
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
find_package_handle_standard_args(jsoncpp DEFAULT_MSG
|
|
||||||
JSONCPP_LIBRARY JSONCPP_INCLUDE_DIR)
|
|
||||||
mark_as_advanced (JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
function(eth_apply TARGET REQUIRED)
|
|
||||||
find_package (Jsoncpp 0.60)
|
|
||||||
eth_show_dependency(JSONCPP JsonCpp)
|
|
||||||
if (JSONCPP_FOUND)
|
|
||||||
target_include_directories(${TARGET} SYSTEM BEFORE PUBLIC ${JSONCPP_INCLUDE_DIRS})
|
|
||||||
target_link_libraries(${TARGET} ${JSONCPP_LIBRARIES})
|
|
||||||
elseif (NOT ${REQUIRED} STREQUAL "OPTIONAL")
|
|
||||||
message(FATAL_ERROR "Jsoncpp library not found")
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
@ -15,8 +15,7 @@ function(eth_apply TARGET REQUIRED SUBMODULE)
|
|||||||
target_include_directories(${TARGET} PUBLIC ${Solidity_INCLUDE_DIRS})
|
target_include_directories(${TARGET} PUBLIC ${Solidity_INCLUDE_DIRS})
|
||||||
|
|
||||||
if (${SUBMODULE} STREQUAL "solevmasm")
|
if (${SUBMODULE} STREQUAL "solevmasm")
|
||||||
eth_use(${TARGET} ${REQUIRED} Jsoncpp)
|
target_link_libraries(${TARGET} ${Solidity_SOLEVMASM_LIBRARIES} jsoncpp)
|
||||||
target_link_libraries(${TARGET} ${Solidity_SOLEVMASM_LIBRARIES})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (${SUBMODULE} STREQUAL "lll")
|
if (${SUBMODULE} STREQUAL "lll")
|
||||||
|
2
deps
2
deps
@ -1 +1 @@
|
|||||||
Subproject commit f2ede70f33633b26a27299ff39995914db2c6923
|
Subproject commit b3db8905894eafb74a436b702de78ba235f3a3b1
|
@ -56,9 +56,9 @@ copyright = '2016, Ethereum'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.4.4'
|
version = '0.4.5'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.4.4-develop'
|
release = '0.4.5-develop'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -2,14 +2,14 @@
|
|||||||
Expressions and Control Structures
|
Expressions and Control Structures
|
||||||
##################################
|
##################################
|
||||||
|
|
||||||
.. index:: if, else, while, for, break, continue, return, switch, goto
|
.. index:: if, else, while, do/while, for, break, continue, return, switch, goto
|
||||||
|
|
||||||
Control Structures
|
Control Structures
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Most of the control structures from C or JavaScript are available in Solidity
|
Most of the control structures from C or JavaScript are available in Solidity
|
||||||
except for ``switch`` and ``goto``. So
|
except for ``switch`` and ``goto``. So
|
||||||
there is: ``if``, ``else``, ``while``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with
|
there is: ``if``, ``else``, ``while``, ``do``, ``for``, ``break``, ``continue``, ``return``, ``? :``, with
|
||||||
the usual semantics known from C or JavaScript.
|
the usual semantics known from C or JavaScript.
|
||||||
|
|
||||||
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
Parentheses can *not* be omitted for conditionals, but curly brances can be omitted
|
||||||
@ -329,9 +329,10 @@ Currently, there are situations, where exceptions happen automatically in Solidi
|
|||||||
3. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
|
3. If you call a function via a message call but it does not finish properly (i.e. it runs out of gas, has no matching function, or throws an exception itself), except when a low level operation ``call``, ``send``, ``delegatecall`` or ``callcode`` is used. The low level operations never throw exceptions but indicate failures by returning ``false``.
|
||||||
4. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
|
4. If you create a contract using the ``new`` keyword but the contract creation does not finish properly (see above for the definition of "not finish properly").
|
||||||
5. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``).
|
5. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``).
|
||||||
6. If you perform an external function call targeting a contract that contains no code.
|
6. If you convert a value too big or negative into an enum type.
|
||||||
7. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
|
7. If you perform an external function call targeting a contract that contains no code.
|
||||||
8. If your contract receives Ether via a public accessor function.
|
8. If your contract receives Ether via a public function without ``payable`` modifier (including the constructor and the fallback function).
|
||||||
|
9. If your contract receives Ether via a public accessor function.
|
||||||
|
|
||||||
Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect.
|
Internally, Solidity performs an "invalid jump" when an exception is thrown and thus causes the EVM to revert all changes made to the state. The reason for this is that there is no safe way to continue execution, because an expected effect did not occur. Because we want to retain the atomicity of transactions, the safest thing to do is to revert all changes and make the whole transaction (or at least call) without effect.
|
||||||
|
|
||||||
@ -426,7 +427,7 @@ these curly braces, the following can be used (see the later sections for more d
|
|||||||
|
|
||||||
- literals, e.g. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
- literals, e.g. ``0x123``, ``42`` or ``"abc"`` (strings up to 32 characters)
|
||||||
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
|
- opcodes (in "instruction style"), e.g. ``mload sload dup1 sstore``, for a list see below
|
||||||
- opcodes in functional style, e.g. ``add(1, mlod(0))``
|
- opcodes in functional style, e.g. ``add(1, mload(0))``
|
||||||
- labels, e.g. ``name:``
|
- labels, e.g. ``name:``
|
||||||
- variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)``
|
- variable declarations, e.g. ``let x := 7`` or ``let x := add(y, 3)``
|
||||||
- identifiers (externals, labels or assembly-local variables), e.g. ``jump(name)``, ``3 x add``
|
- identifiers (externals, labels or assembly-local variables), e.g. ``jump(name)``, ``3 x add``
|
||||||
@ -715,6 +716,10 @@ will have a wrong impression about the stack height at label ``two``:
|
|||||||
three:
|
three:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
``invalidJumpLabel`` is a pre-defined label. Jumping to this location will always
|
||||||
|
result in an invalid jump, effectively aborting execution of the code.
|
||||||
|
|
||||||
Declaring Assembly-Local Variables
|
Declaring Assembly-Local Variables
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
@ -25,7 +25,7 @@ Storage
|
|||||||
storedData = x;
|
storedData = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
function get() constant returns (uint retVal) {
|
function get() constant returns (uint) {
|
||||||
return storedData;
|
return storedData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,7 +136,7 @@ like this one. The accessor function created by the ``public`` keyword
|
|||||||
is a bit more complex in this case. It roughly looks like the
|
is a bit more complex in this case. It roughly looks like the
|
||||||
following::
|
following::
|
||||||
|
|
||||||
function balances(address _account) returns (uint balance) {
|
function balances(address _account) returns (uint) {
|
||||||
return balances[_account];
|
return balances[_account];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +170,15 @@ of votes.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calls winningProposal() function to get the index
|
||||||
|
// of the winner contained in the proposals array and then
|
||||||
|
// returns the name of the winner
|
||||||
|
function winnerName() constant
|
||||||
|
returns (bytes32 winnerName)
|
||||||
|
{
|
||||||
|
winnerName = proposals[winningProposal()].name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Possible Improvements
|
Possible Improvements
|
||||||
|
127
docs/types.rst
127
docs/types.rst
@ -234,10 +234,11 @@ Hexademical Literals behave like String Literals and have the same convertibilit
|
|||||||
.. _enums:
|
.. _enums:
|
||||||
|
|
||||||
Enums
|
Enums
|
||||||
=====
|
-----
|
||||||
|
|
||||||
Enums are one way to create a user-defined type in Solidity. They are explicitly convertible
|
Enums are one way to create a user-defined type in Solidity. They are explicitly convertible
|
||||||
to and from all integer types but implicit conversion is not allowed.
|
to and from all integer types but implicit conversion is not allowed. The explicit conversions
|
||||||
|
check the value ranges at runtime and a failure causes an exception. Enums needs at least one member.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@ -266,6 +267,128 @@ to and from all integer types but implicit conversion is not allowed.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.. index:: ! function type, ! type; function
|
||||||
|
|
||||||
|
.. _function_types:
|
||||||
|
|
||||||
|
Function Types
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Function types are the types of functions. Variables of function type
|
||||||
|
can be assigned from functions and function parameters of function type
|
||||||
|
can be used to pass functions to and return functions from function calls.
|
||||||
|
Function types come in two flavours - *internal* and *external* functions:
|
||||||
|
|
||||||
|
Internal functions can only be used inside the current contract (more specifically,
|
||||||
|
inside the current code unit, which also includes internal library functions
|
||||||
|
and inherited functions) because they cannot be executed outside of the
|
||||||
|
context of the current contract. Calling an internal function is realized
|
||||||
|
by jumping to its entry label, just like when calling a function of the current
|
||||||
|
contract internally.
|
||||||
|
|
||||||
|
External functions consist of an address and a function signature and they can
|
||||||
|
be passed via and returned from external function calls.
|
||||||
|
|
||||||
|
Function types are notated as follows::
|
||||||
|
|
||||||
|
function (<parameter types>) {internal|external} [constant] [payable] [returns (<return types>)]
|
||||||
|
|
||||||
|
In contrast to the parameter types, the return types cannot be empty - if the
|
||||||
|
function type should not return anything, the whole ``returns (<return types>)``
|
||||||
|
part has to be omitted.
|
||||||
|
|
||||||
|
By default, function types are internal, so the ``internal`` keyword can be
|
||||||
|
omitted.
|
||||||
|
|
||||||
|
There are two ways to access a function in the current contract: Either directly
|
||||||
|
by its name, ``f``, or using ``this.f``. The former will result in an internal
|
||||||
|
function, the latter in an external function.
|
||||||
|
|
||||||
|
If a function type variable is not initialized, calling it will result
|
||||||
|
in an exception. The same happens if you call a function after using ``delete``
|
||||||
|
on it.
|
||||||
|
|
||||||
|
If external function types are used outside of the context of Solidity,
|
||||||
|
they are treated as the ``function`` type, which encodes the address
|
||||||
|
followed by the function identifier together in a single ``bytes24`` type.
|
||||||
|
|
||||||
|
Example that shows how to use internal function types::
|
||||||
|
|
||||||
|
library ArrayUtils {
|
||||||
|
// internal functions can be used in internal library functions because
|
||||||
|
// they will be part of the same code context
|
||||||
|
function map(uint[] memory self, function (uint) returns (uint) f)
|
||||||
|
returns (uint[] memory r)
|
||||||
|
{
|
||||||
|
r = new uint[](self.length);
|
||||||
|
for (uint i = 0; i < self.length; i++) {
|
||||||
|
r[i] = f(self[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function reduce(
|
||||||
|
uint[] memory self,
|
||||||
|
function (uint) returns (uint) f
|
||||||
|
)
|
||||||
|
returns (uint r)
|
||||||
|
{
|
||||||
|
r = self[0];
|
||||||
|
for (uint i = 1; i < self.length; i++) {
|
||||||
|
r = f(r, self[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function range(uint length) returns (uint[] memory r) {
|
||||||
|
r = new uint[](length);
|
||||||
|
for (uint i = 0; i < r.length; i++) {
|
||||||
|
r[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract Pyramid {
|
||||||
|
using ArrayUtils for *;
|
||||||
|
function pyramid(uint l) return (uint) {
|
||||||
|
return ArrayUtils.range(l).map(square).reduce(sum);
|
||||||
|
}
|
||||||
|
function square(uint x) internal returns (uint) {
|
||||||
|
return x * x;
|
||||||
|
}
|
||||||
|
function sum(uint x, uint y) internal returns (uint) {
|
||||||
|
return x + y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Another example that uses external function types::
|
||||||
|
|
||||||
|
contract Oracle {
|
||||||
|
struct Request {
|
||||||
|
bytes data;
|
||||||
|
function(bytes) external callback;
|
||||||
|
}
|
||||||
|
Request[] requests;
|
||||||
|
event NewRequest(uint);
|
||||||
|
function query(bytes data, function(bytes) external callback) {
|
||||||
|
requests.push(Request(data, callback));
|
||||||
|
NewRequest(requests.length - 1);
|
||||||
|
}
|
||||||
|
function reply(uint requestID, bytes response) {
|
||||||
|
// Here goes the check that the reply comes from a trusted source
|
||||||
|
requests[requestID].callback(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract OracleUser {
|
||||||
|
Oracle constant oracle = 0x1234567; // known contract
|
||||||
|
function buySomething() {
|
||||||
|
oracle.query("USD", oracleResponse);
|
||||||
|
}
|
||||||
|
function oracleResponse(bytes response) {
|
||||||
|
if (msg.sender != oracle) throw;
|
||||||
|
// Use the data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Note that lambda or inline functions are planned but not yet supported.
|
||||||
|
|
||||||
.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
|
.. index:: ! type;reference, ! reference type, storage, memory, location, array, struct
|
||||||
|
|
||||||
Reference Types
|
Reference Types
|
||||||
|
@ -50,7 +50,7 @@ namespace and are mainly used to provide information about the blockchain.
|
|||||||
Block and Transaction Properties
|
Block and Transaction Properties
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
|
||||||
- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks
|
- ``block.blockhash(uint blockNumber) returns (bytes32)``: hash of the given block - only works for 256 most recent blocks excluding current
|
||||||
- ``block.coinbase`` (``address``): current block miner's address
|
- ``block.coinbase`` (``address``): current block miner's address
|
||||||
- ``block.difficulty`` (``uint``): current block difficulty
|
- ``block.difficulty`` (``uint``): current block difficulty
|
||||||
- ``block.gaslimit`` (``uint``): current block gaslimit
|
- ``block.gaslimit`` (``uint``): current block gaslimit
|
||||||
|
@ -70,7 +70,6 @@
|
|||||||
|
|
||||||
#include "vector_ref.h"
|
#include "vector_ref.h"
|
||||||
|
|
||||||
// CryptoPP defines byte in the global namespace, so must we.
|
|
||||||
using byte = uint8_t;
|
using byte = uint8_t;
|
||||||
|
|
||||||
// Quote a given token stream to turn it into a string.
|
// Quote a given token stream to turn it into a string.
|
||||||
|
@ -20,9 +20,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CommonData.h"
|
#include "CommonData.h"
|
||||||
#if defined(_MSC_VER)
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
#include "Exceptions.h"
|
#include "Exceptions.h"
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
|
44
libdevcore/JSON.h
Normal file
44
libdevcore/JSON.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file JSON.h
|
||||||
|
* @date 2016
|
||||||
|
*
|
||||||
|
* JSON related helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Serialise the JSON object (@a _input) with identation
|
||||||
|
std::string jsonPrettyPrint(Json::Value const& _input)
|
||||||
|
{
|
||||||
|
return Json::StyledWriter().write(_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serialise theJ SON object (@a _input) without identation
|
||||||
|
std::string jsonCompactPrint(Json::Value const& _input)
|
||||||
|
{
|
||||||
|
Json::FastWriter writer;
|
||||||
|
writer.omitEndingLineFeed();
|
||||||
|
return writer.write(_input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
63
libdevcore/SwarmHash.cpp
Normal file
63
libdevcore/SwarmHash.cpp
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file SwarmHash.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libdevcore/SwarmHash.h>
|
||||||
|
|
||||||
|
#include <libdevcore/SHA3.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
|
|
||||||
|
|
||||||
|
bytes toLittleEndian(size_t _size)
|
||||||
|
{
|
||||||
|
bytes encoded(8);
|
||||||
|
for (size_t i = 0; i < 8; ++i)
|
||||||
|
encoded[i] = (_size >> (8 * i)) & 0xff;
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
h256 swarmHashSimple(bytesConstRef _data, size_t _size)
|
||||||
|
{
|
||||||
|
return keccak256(toLittleEndian(_size) + _data.toBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
h256 swarmHashIntermediate(bytes const& _input, size_t _offset, size_t _length)
|
||||||
|
{
|
||||||
|
if (_length <= 0x1000)
|
||||||
|
return swarmHashSimple(bytesConstRef(_input.data() + _offset, _length), _length);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bytes innerNodes;
|
||||||
|
size_t maxRepresentedSize = 0x1000;
|
||||||
|
while (maxRepresentedSize * (0x1000 / 32) < _length)
|
||||||
|
maxRepresentedSize *= (0x1000 / 32);
|
||||||
|
for (size_t i = 0; i < _length; i += maxRepresentedSize)
|
||||||
|
{
|
||||||
|
size_t size = std::min(maxRepresentedSize, _length - i);
|
||||||
|
innerNodes += swarmHashIntermediate(_input, _offset + i, size).asBytes();
|
||||||
|
}
|
||||||
|
return swarmHashSimple(bytesConstRef(&innerNodes), _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h256 dev::swarmHash(bytes const& _input)
|
||||||
|
{
|
||||||
|
return swarmHashIntermediate(_input, 0, _input.size());
|
||||||
|
}
|
31
libdevcore/SwarmHash.h
Normal file
31
libdevcore/SwarmHash.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/** @file SwarmHash.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <libdevcore/FixedHash.h>
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Compute the "swarm hash" of @a _data
|
||||||
|
h256 swarmHash(bytes const& _data);
|
||||||
|
|
||||||
|
}
|
@ -20,13 +20,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Assembly.h"
|
#include "Assembly.h"
|
||||||
#include <fstream>
|
|
||||||
#include <libevmasm/CommonSubexpressionEliminator.h>
|
#include <libevmasm/CommonSubexpressionEliminator.h>
|
||||||
#include <libevmasm/ControlFlowGraph.h>
|
#include <libevmasm/ControlFlowGraph.h>
|
||||||
|
#include <libevmasm/PeepholeOptimiser.h>
|
||||||
#include <libevmasm/BlockDeduplicator.h>
|
#include <libevmasm/BlockDeduplicator.h>
|
||||||
#include <libevmasm/ConstantOptimiser.h>
|
#include <libevmasm/ConstantOptimiser.h>
|
||||||
#include <libevmasm/GasMeter.h>
|
#include <libevmasm/GasMeter.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
@ -75,17 +79,17 @@ string Assembly::out() const
|
|||||||
return ret.str();
|
return ret.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned Assembly::bytesRequired() const
|
unsigned Assembly::bytesRequired(unsigned subTagSize) const
|
||||||
{
|
{
|
||||||
for (unsigned br = 1;; ++br)
|
for (unsigned tagSize = subTagSize; true; ++tagSize)
|
||||||
{
|
{
|
||||||
unsigned ret = 1;
|
unsigned ret = 1;
|
||||||
for (auto const& i: m_data)
|
for (auto const& i: m_data)
|
||||||
ret += i.second.size();
|
ret += i.second.size();
|
||||||
|
|
||||||
for (AssemblyItem const& i: m_items)
|
for (AssemblyItem const& i: m_items)
|
||||||
ret += i.bytesRequired(br);
|
ret += i.bytesRequired(tagSize);
|
||||||
if (dev::bytesRequired(ret) <= br)
|
if (dev::bytesRequired(ret) <= tagSize)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -132,13 +136,19 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
|
|||||||
if (i.data() == 0)
|
if (i.data() == 0)
|
||||||
_out << " PUSH [ErrorTag]";
|
_out << " PUSH [ErrorTag]";
|
||||||
else
|
else
|
||||||
_out << " PUSH [tag" << dec << i.data() << "]";
|
{
|
||||||
|
size_t subId = i.splitForeignPushTag().first;
|
||||||
|
if (subId == size_t(-1))
|
||||||
|
_out << " PUSH [tag" << dec << i.splitForeignPushTag().second << "]";
|
||||||
|
else
|
||||||
|
_out << " PUSH [tag" << dec << subId << ":" << i.splitForeignPushTag().second << "]";
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case PushSub:
|
case PushSub:
|
||||||
_out << " PUSH [$" << h256(i.data()).abridgedMiddle() << "]";
|
_out << " PUSH [$" << size_t(i.data()) << "]";
|
||||||
break;
|
break;
|
||||||
case PushSubSize:
|
case PushSubSize:
|
||||||
_out << " PUSH #[$" << h256(i.data()).abridgedMiddle() << "]";
|
_out << " PUSH #[$" << size_t(i.data()) << "]";
|
||||||
break;
|
break;
|
||||||
case PushProgramSize:
|
case PushProgramSize:
|
||||||
_out << " PUSHSIZE";
|
_out << " PUSHSIZE";
|
||||||
@ -167,7 +177,7 @@ ostream& Assembly::streamAsm(ostream& _out, string const& _prefix, StringMap con
|
|||||||
for (size_t i = 0; i < m_subs.size(); ++i)
|
for (size_t i = 0; i < m_subs.size(); ++i)
|
||||||
{
|
{
|
||||||
_out << _prefix << " " << hex << i << ": " << endl;
|
_out << _prefix << " " << hex << i << ": " << endl;
|
||||||
m_subs[i].stream(_out, _prefix + " ", _sourceCodes);
|
m_subs[i]->stream(_out, _prefix + " ", _sourceCodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _out;
|
return _out;
|
||||||
@ -266,7 +276,7 @@ Json::Value Assembly::streamAsmJson(ostream& _out, StringMap const& _sourceCodes
|
|||||||
{
|
{
|
||||||
std::stringstream hexStr;
|
std::stringstream hexStr;
|
||||||
hexStr << hex << i;
|
hexStr << hex << i;
|
||||||
data[hexStr.str()] = m_subs[i].stream(_out, "", _sourceCodes, true);
|
data[hexStr.str()] = m_subs[i]->stream(_out, "", _sourceCodes, true);
|
||||||
}
|
}
|
||||||
root[".data"] = data;
|
root[".data"] = data;
|
||||||
_out << root;
|
_out << root;
|
||||||
@ -308,41 +318,67 @@ void Assembly::injectStart(AssemblyItem const& _i)
|
|||||||
|
|
||||||
Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
||||||
{
|
{
|
||||||
if (!_enable)
|
optimiseInternal(_enable, _isCreation, _runs);
|
||||||
return *this;
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
map<u256, u256> Assembly::optimiseInternal(bool _enable, bool _isCreation, size_t _runs)
|
||||||
|
{
|
||||||
|
for (size_t subId = 0; subId < m_subs.size(); ++subId)
|
||||||
|
{
|
||||||
|
map<u256, u256> subTagReplacements = m_subs[subId]->optimiseInternal(_enable, false, _runs);
|
||||||
|
BlockDeduplicator::applyTagReplacement(m_items, subTagReplacements, subId);
|
||||||
|
}
|
||||||
|
|
||||||
|
map<u256, u256> tagReplacements;
|
||||||
unsigned total = 0;
|
unsigned total = 0;
|
||||||
for (unsigned count = 1; count > 0; total += count)
|
for (unsigned count = 1; count > 0; total += count)
|
||||||
{
|
{
|
||||||
count = 0;
|
count = 0;
|
||||||
|
|
||||||
|
PeepholeOptimiser peepOpt(m_items);
|
||||||
|
if (peepOpt.optimise())
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (!_enable)
|
||||||
|
continue;
|
||||||
|
|
||||||
// This only modifies PushTags, we have to run again to actually remove code.
|
// This only modifies PushTags, we have to run again to actually remove code.
|
||||||
BlockDeduplicator dedup(m_items);
|
BlockDeduplicator dedup(m_items);
|
||||||
if (dedup.deduplicate())
|
if (dedup.deduplicate())
|
||||||
|
{
|
||||||
|
tagReplacements.insert(dedup.replacedTags().begin(), dedup.replacedTags().end());
|
||||||
count++;
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// Control flow graph that resets knowledge at path joins.
|
// Control flow graph optimization has been here before but is disabled because it
|
||||||
ControlFlowGraph cfg(m_items, false);
|
// assumes we only jump to tags that are pushed. This is not the case anymore with
|
||||||
|
// function types that can be stored in storage.
|
||||||
AssemblyItems optimisedItems;
|
AssemblyItems optimisedItems;
|
||||||
for (BasicBlock const& block: cfg.optimisedBlocks())
|
|
||||||
|
auto iter = m_items.begin();
|
||||||
|
while (iter != m_items.end())
|
||||||
{
|
{
|
||||||
// We used to start with the block's initial state but it caused
|
auto end = iter;
|
||||||
// too many inconsistencies.
|
while (end != m_items.end())
|
||||||
|
if (SemanticInformation::altersControlFlow(*end++))
|
||||||
|
break;
|
||||||
|
|
||||||
KnownState emptyState;
|
KnownState emptyState;
|
||||||
CommonSubexpressionEliminator eliminator(emptyState);
|
CommonSubexpressionEliminator eliminator(emptyState);
|
||||||
auto iter = m_items.begin() + block.begin;
|
auto blockIter = iter;
|
||||||
auto const end = m_items.begin() + block.end;
|
auto const blockEnd = end;
|
||||||
while (iter < end)
|
while (blockIter < blockEnd)
|
||||||
{
|
{
|
||||||
auto orig = iter;
|
auto orig = blockIter;
|
||||||
iter = eliminator.feedItems(iter, end);
|
blockIter = eliminator.feedItems(blockIter, blockEnd);
|
||||||
bool shouldReplace = false;
|
bool shouldReplace = false;
|
||||||
AssemblyItems optimisedChunk;
|
AssemblyItems optimisedChunk;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
optimisedChunk = eliminator.getOptimizedItems();
|
optimisedChunk = eliminator.getOptimizedItems();
|
||||||
shouldReplace = (optimisedChunk.size() < size_t(iter - orig));
|
shouldReplace = (optimisedChunk.size() < size_t(blockIter - orig));
|
||||||
}
|
}
|
||||||
catch (StackTooDeepException const&)
|
catch (StackTooDeepException const&)
|
||||||
{
|
{
|
||||||
@ -361,10 +397,10 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
|||||||
optimisedItems += optimisedChunk;
|
optimisedItems += optimisedChunk;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
copy(orig, iter, back_inserter(optimisedItems));
|
copy(orig, blockIter, back_inserter(optimisedItems));
|
||||||
}
|
}
|
||||||
|
iter = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optimisedItems.size() < m_items.size())
|
if (optimisedItems.size() < m_items.size())
|
||||||
{
|
{
|
||||||
m_items = move(optimisedItems);
|
m_items = move(optimisedItems);
|
||||||
@ -373,17 +409,15 @@ Assembly& Assembly::optimise(bool _enable, bool _isCreation, size_t _runs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
total += ConstantOptimisationMethod::optimiseConstants(
|
if (_enable)
|
||||||
_isCreation,
|
total += ConstantOptimisationMethod::optimiseConstants(
|
||||||
_isCreation ? 1 : _runs,
|
_isCreation,
|
||||||
*this,
|
_isCreation ? 1 : _runs,
|
||||||
m_items
|
*this,
|
||||||
);
|
m_items
|
||||||
|
);
|
||||||
|
|
||||||
for (auto& sub: m_subs)
|
return tagReplacements;
|
||||||
sub.optimise(true, false, _runs);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkerObject const& Assembly::assemble() const
|
LinkerObject const& Assembly::assemble() const
|
||||||
@ -391,20 +425,28 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
if (!m_assembledObject.bytecode.empty())
|
if (!m_assembledObject.bytecode.empty())
|
||||||
return m_assembledObject;
|
return m_assembledObject;
|
||||||
|
|
||||||
|
size_t subTagSize = 1;
|
||||||
|
for (auto const& sub: m_subs)
|
||||||
|
{
|
||||||
|
sub->assemble();
|
||||||
|
if (!sub->m_tagPositionsInBytecode.empty())
|
||||||
|
subTagSize = max(subTagSize, *max_element(sub->m_tagPositionsInBytecode.begin(), sub->m_tagPositionsInBytecode.end()));
|
||||||
|
}
|
||||||
|
|
||||||
LinkerObject& ret = m_assembledObject;
|
LinkerObject& ret = m_assembledObject;
|
||||||
|
|
||||||
unsigned totalBytes = bytesRequired();
|
size_t bytesRequiredForCode = bytesRequired(subTagSize);
|
||||||
vector<unsigned> tagPos(m_usedTags);
|
m_tagPositionsInBytecode = vector<size_t>(m_usedTags, -1);
|
||||||
map<unsigned, unsigned> tagRef;
|
map<size_t, pair<size_t, size_t>> tagRef;
|
||||||
multimap<h256, unsigned> dataRef;
|
multimap<h256, unsigned> dataRef;
|
||||||
multimap<size_t, size_t> subRef;
|
multimap<size_t, size_t> subRef;
|
||||||
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
|
vector<unsigned> sizeRef; ///< Pointers to code locations where the size of the program is inserted
|
||||||
unsigned bytesPerTag = dev::bytesRequired(totalBytes);
|
unsigned bytesPerTag = dev::bytesRequired(bytesRequiredForCode);
|
||||||
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
|
byte tagPush = (byte)Instruction::PUSH1 - 1 + bytesPerTag;
|
||||||
|
|
||||||
unsigned bytesRequiredIncludingData = bytesRequired();
|
unsigned bytesRequiredIncludingData = bytesRequiredForCode + 1;
|
||||||
for (auto const& sub: m_subs)
|
for (auto const& sub: m_subs)
|
||||||
bytesRequiredIncludingData += sub.assemble().bytecode.size();
|
bytesRequiredIncludingData += sub->assemble().bytecode.size();
|
||||||
|
|
||||||
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);
|
unsigned bytesPerDataRef = dev::bytesRequired(bytesRequiredIncludingData);
|
||||||
byte dataRefPush = (byte)Instruction::PUSH1 - 1 + bytesPerDataRef;
|
byte dataRefPush = (byte)Instruction::PUSH1 - 1 + bytesPerDataRef;
|
||||||
@ -413,8 +455,8 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
for (AssemblyItem const& i: m_items)
|
for (AssemblyItem const& i: m_items)
|
||||||
{
|
{
|
||||||
// store position of the invalid jump destination
|
// store position of the invalid jump destination
|
||||||
if (i.type() != Tag && tagPos[0] == 0)
|
if (i.type() != Tag && m_tagPositionsInBytecode[0] == size_t(-1))
|
||||||
tagPos[0] = ret.bytecode.size();
|
m_tagPositionsInBytecode[0] = ret.bytecode.size();
|
||||||
|
|
||||||
switch (i.type())
|
switch (i.type())
|
||||||
{
|
{
|
||||||
@ -446,7 +488,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
case PushTag:
|
case PushTag:
|
||||||
{
|
{
|
||||||
ret.bytecode.push_back(tagPush);
|
ret.bytecode.push_back(tagPush);
|
||||||
tagRef[ret.bytecode.size()] = (unsigned)i.data();
|
tagRef[ret.bytecode.size()] = i.splitForeignPushTag();
|
||||||
ret.bytecode.resize(ret.bytecode.size() + bytesPerTag);
|
ret.bytecode.resize(ret.bytecode.size() + bytesPerTag);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -462,7 +504,7 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
break;
|
break;
|
||||||
case PushSubSize:
|
case PushSubSize:
|
||||||
{
|
{
|
||||||
auto s = m_subs.at(size_t(i.data())).assemble().bytecode.size();
|
auto s = m_subs.at(size_t(i.data()))->assemble().bytecode.size();
|
||||||
i.setPushedValue(u256(s));
|
i.setPushedValue(u256(s));
|
||||||
byte b = max<unsigned>(1, dev::bytesRequired(s));
|
byte b = max<unsigned>(1, dev::bytesRequired(s));
|
||||||
ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b);
|
ret.bytecode.push_back((byte)Instruction::PUSH1 - 1 + b);
|
||||||
@ -484,25 +526,16 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
ret.bytecode.resize(ret.bytecode.size() + 20);
|
ret.bytecode.resize(ret.bytecode.size() + 20);
|
||||||
break;
|
break;
|
||||||
case Tag:
|
case Tag:
|
||||||
tagPos[(unsigned)i.data()] = ret.bytecode.size();
|
|
||||||
assertThrow(i.data() != 0, AssemblyException, "");
|
assertThrow(i.data() != 0, AssemblyException, "");
|
||||||
|
assertThrow(i.splitForeignPushTag().first == size_t(-1), AssemblyException, "Foreign tag.");
|
||||||
|
assertThrow(ret.bytecode.size() < 0xffffffffL, AssemblyException, "Tag too large.");
|
||||||
|
m_tagPositionsInBytecode[size_t(i.data())] = ret.bytecode.size();
|
||||||
ret.bytecode.push_back((byte)Instruction::JUMPDEST);
|
ret.bytecode.push_back((byte)Instruction::JUMPDEST);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BOOST_THROW_EXCEPTION(InvalidOpcode());
|
BOOST_THROW_EXCEPTION(InvalidOpcode());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto const& i: tagRef)
|
|
||||||
{
|
|
||||||
bytesRef r(ret.bytecode.data() + i.first, bytesPerTag);
|
|
||||||
auto tag = i.second;
|
|
||||||
if (tag >= tagPos.size())
|
|
||||||
tag = 0;
|
|
||||||
if (tag == 0)
|
|
||||||
assertThrow(tagPos[tag] != 0, AssemblyException, "");
|
|
||||||
|
|
||||||
toBigEndian(tagPos[tag], r);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dataRef.empty() && !subRef.empty())
|
if (!dataRef.empty() && !subRef.empty())
|
||||||
ret.bytecode.push_back(0);
|
ret.bytecode.push_back(0);
|
||||||
@ -516,7 +549,24 @@ LinkerObject const& Assembly::assemble() const
|
|||||||
bytesRef r(ret.bytecode.data() + ref->second, bytesPerDataRef);
|
bytesRef r(ret.bytecode.data() + ref->second, bytesPerDataRef);
|
||||||
toBigEndian(ret.bytecode.size(), r);
|
toBigEndian(ret.bytecode.size(), r);
|
||||||
}
|
}
|
||||||
ret.append(m_subs[i].assemble());
|
ret.append(m_subs[i]->assemble());
|
||||||
|
}
|
||||||
|
for (auto const& i: tagRef)
|
||||||
|
{
|
||||||
|
size_t subId;
|
||||||
|
size_t tagId;
|
||||||
|
tie(subId, tagId) = i.second;
|
||||||
|
assertThrow(subId == size_t(-1) || subId < m_subs.size(), AssemblyException, "Invalid sub id");
|
||||||
|
std::vector<size_t> const& tagPositions =
|
||||||
|
subId == size_t(-1) ?
|
||||||
|
m_tagPositionsInBytecode :
|
||||||
|
m_subs[subId]->m_tagPositionsInBytecode;
|
||||||
|
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.");
|
||||||
|
bytesRef r(ret.bytecode.data() + i.first, bytesPerTag);
|
||||||
|
toBigEndian(pos, r);
|
||||||
}
|
}
|
||||||
for (auto const& dataItem: m_data)
|
for (auto const& dataItem: m_data)
|
||||||
{
|
{
|
||||||
|
@ -14,30 +14,32 @@
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
/** @file Assembly.h
|
|
||||||
* @author Gav Wood <i@gavwood.com>
|
|
||||||
* @date 2014
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <libdevcore/Common.h>
|
|
||||||
#include <libdevcore/Assertions.h>
|
|
||||||
#include <libdevcore/SHA3.h>
|
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/SourceLocation.h>
|
#include <libevmasm/SourceLocation.h>
|
||||||
#include <libevmasm/AssemblyItem.h>
|
#include <libevmasm/AssemblyItem.h>
|
||||||
#include <libevmasm/LinkerObject.h>
|
#include <libevmasm/LinkerObject.h>
|
||||||
#include "Exceptions.h"
|
#include <libevmasm/Exceptions.h>
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
#include <libdevcore/Assertions.h>
|
||||||
|
#include <libdevcore/SHA3.h>
|
||||||
|
|
||||||
#include <json/json.h>
|
#include <json/json.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
namespace eth
|
namespace eth
|
||||||
{
|
{
|
||||||
|
|
||||||
|
using AssemblyPointer = std::shared_ptr<Assembly>;
|
||||||
|
|
||||||
class Assembly
|
class Assembly
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -46,20 +48,18 @@ public:
|
|||||||
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
|
AssemblyItem newTag() { return AssemblyItem(Tag, m_usedTags++); }
|
||||||
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
|
AssemblyItem newPushTag() { return AssemblyItem(PushTag, m_usedTags++); }
|
||||||
AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
AssemblyItem newData(bytes const& _data) { h256 h(dev::keccak256(asString(_data))); m_data[h] = _data; return AssemblyItem(PushData, h); }
|
||||||
AssemblyItem newSub(Assembly const& _sub) { m_subs.push_back(_sub); return AssemblyItem(PushSub, m_subs.size() - 1); }
|
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 const& sub(size_t _sub) const { return *m_subs.at(_sub); }
|
||||||
Assembly& sub(size_t _sub) { return m_subs.at(_sub); }
|
Assembly& sub(size_t _sub) { return *m_subs.at(_sub); }
|
||||||
AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); }
|
AssemblyItem newPushString(std::string const& _data) { h256 h(dev::keccak256(_data)); m_strings[h] = _data; return AssemblyItem(PushString, h); }
|
||||||
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
|
AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); }
|
||||||
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
AssemblyItem newPushLibraryAddress(std::string const& _identifier);
|
||||||
|
|
||||||
AssemblyItem append() { return append(newTag()); }
|
|
||||||
void append(Assembly const& _a);
|
void append(Assembly const& _a);
|
||||||
void append(Assembly const& _a, int _deposit);
|
void append(Assembly const& _a, int _deposit);
|
||||||
AssemblyItem const& append(AssemblyItem const& _i);
|
AssemblyItem const& append(AssemblyItem const& _i);
|
||||||
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
|
AssemblyItem const& append(std::string const& _data) { return append(newPushString(_data)); }
|
||||||
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); }
|
||||||
AssemblyItem appendSubSize(Assembly const& _a) { auto ret = newSub(_a); append(newPushSubSize(ret.data())); return ret; }
|
|
||||||
/// Pushes the final size of the current assembly itself. Use this when the code is modified
|
/// Pushes the final size of the current assembly itself. Use this when the code is modified
|
||||||
/// after compilation and CODESIZE is not an option.
|
/// after compilation and CODESIZE is not an option.
|
||||||
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
|
void appendProgramSize() { append(AssemblyItem(PushProgramSize)); }
|
||||||
@ -101,6 +101,7 @@ public:
|
|||||||
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
|
/// execution gas usage is optimised. @a _isCreation should be true for the top-level assembly.
|
||||||
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
|
/// @a _runs specifes an estimate on how often each opcode in this assembly will be executed,
|
||||||
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
|
/// i.e. use a small value to optimise for size and a large value to optimise for runtime.
|
||||||
|
/// If @a _enable is not set, will perform some simple peephole optimizations.
|
||||||
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
|
Assembly& optimise(bool _enable, bool _isCreation = true, size_t _runs = 200);
|
||||||
Json::Value stream(
|
Json::Value stream(
|
||||||
std::ostream& _out,
|
std::ostream& _out,
|
||||||
@ -110,9 +111,13 @@ public:
|
|||||||
) const;
|
) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// Does the same operations as @a optimise, but should only be applied to a sub and
|
||||||
|
/// returns the replaced tags.
|
||||||
|
std::map<u256, u256> optimiseInternal(bool _enable, bool _isCreation, size_t _runs);
|
||||||
|
|
||||||
std::string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
|
std::string locationFromSources(StringMap const& _sourceCodes, SourceLocation const& _location) const;
|
||||||
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
|
void donePath() { if (m_totalDeposit != INT_MAX && m_totalDeposit != m_deposit) BOOST_THROW_EXCEPTION(InvalidDeposit()); }
|
||||||
unsigned bytesRequired() const;
|
unsigned bytesRequired(unsigned subTagSize) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Json::Value streamAsmJson(std::ostream& _out, StringMap const& _sourceCodes) const;
|
Json::Value streamAsmJson(std::ostream& _out, StringMap const& _sourceCodes) const;
|
||||||
@ -124,11 +129,12 @@ protected:
|
|||||||
unsigned m_usedTags = 1;
|
unsigned m_usedTags = 1;
|
||||||
AssemblyItems m_items;
|
AssemblyItems m_items;
|
||||||
std::map<h256, bytes> m_data;
|
std::map<h256, bytes> m_data;
|
||||||
std::vector<Assembly> m_subs;
|
std::vector<std::shared_ptr<Assembly>> m_subs;
|
||||||
std::map<h256, std::string> m_strings;
|
std::map<h256, std::string> m_strings;
|
||||||
std::map<h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
std::map<h256, std::string> m_libraries; ///< Identifiers of libraries to be linked.
|
||||||
|
|
||||||
mutable LinkerObject m_assembledObject;
|
mutable LinkerObject m_assembledObject;
|
||||||
|
mutable std::vector<size_t> m_tagPositionsInBytecode;
|
||||||
|
|
||||||
int m_deposit = 0;
|
int m_deposit = 0;
|
||||||
int m_baseDeposit = 0;
|
int m_baseDeposit = 0;
|
||||||
|
@ -26,6 +26,29 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
|
|
||||||
|
AssemblyItem AssemblyItem::toSubAssemblyTag(size_t _subId) const
|
||||||
|
{
|
||||||
|
assertThrow(m_data < (u256(1) << 64), Exception, "Tag already has subassembly set.");
|
||||||
|
|
||||||
|
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
|
||||||
|
AssemblyItem r = *this;
|
||||||
|
r.m_type = PushTag;
|
||||||
|
r.setPushTagSubIdAndTag(_subId, size_t(m_data));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
pair<size_t, size_t> AssemblyItem::splitForeignPushTag() const
|
||||||
|
{
|
||||||
|
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
|
||||||
|
return make_pair(size_t(m_data / (u256(1) << 64)) - 1, size_t(m_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssemblyItem::setPushTagSubIdAndTag(size_t _subId, size_t _tag)
|
||||||
|
{
|
||||||
|
assertThrow(m_type == PushTag || m_type == Tag, Exception, "");
|
||||||
|
setData(_tag + (u256(_subId + 1) << 64));
|
||||||
|
}
|
||||||
|
|
||||||
unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
|
unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const
|
||||||
{
|
{
|
||||||
switch (m_type)
|
switch (m_type)
|
||||||
@ -104,8 +127,14 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
|
|||||||
_out << " PushString" << hex << (unsigned)_item.data();
|
_out << " PushString" << hex << (unsigned)_item.data();
|
||||||
break;
|
break;
|
||||||
case PushTag:
|
case PushTag:
|
||||||
_out << " PushTag " << _item.data();
|
{
|
||||||
|
size_t subId = _item.splitForeignPushTag().first;
|
||||||
|
if (subId == size_t(-1))
|
||||||
|
_out << " PushTag " << _item.splitForeignPushTag().second;
|
||||||
|
else
|
||||||
|
_out << " PushTag " << subId << ":" << _item.splitForeignPushTag().second;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case Tag:
|
case Tag:
|
||||||
_out << " Tag " << _item.data();
|
_out << " Tag " << _item.data();
|
||||||
break;
|
break;
|
||||||
@ -113,10 +142,10 @@ ostream& dev::eth::operator<<(ostream& _out, AssemblyItem const& _item)
|
|||||||
_out << " PushData " << hex << (unsigned)_item.data();
|
_out << " PushData " << hex << (unsigned)_item.data();
|
||||||
break;
|
break;
|
||||||
case PushSub:
|
case PushSub:
|
||||||
_out << " PushSub " << hex << h256(_item.data()).abridgedMiddle();
|
_out << " PushSub " << hex << size_t(_item.data());
|
||||||
break;
|
break;
|
||||||
case PushSubSize:
|
case PushSubSize:
|
||||||
_out << " PushSubSize " << hex << h256(_item.data()).abridgedMiddle();
|
_out << " PushSubSize " << hex << size_t(_item.data());
|
||||||
break;
|
break;
|
||||||
case PushProgramSize:
|
case PushProgramSize:
|
||||||
_out << " PushProgramSize";
|
_out << " PushProgramSize";
|
||||||
|
@ -69,6 +69,14 @@ public:
|
|||||||
|
|
||||||
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, m_data); }
|
AssemblyItem tag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(Tag, m_data); }
|
||||||
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, m_data); }
|
AssemblyItem pushTag() const { assertThrow(m_type == PushTag || m_type == Tag, Exception, ""); return AssemblyItem(PushTag, m_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;
|
||||||
|
/// @returns splits the data of the push tag into sub assembly id and actual tag id.
|
||||||
|
/// The sub assembly id of non-foreign push tags is -1.
|
||||||
|
std::pair<size_t, size_t> splitForeignPushTag() const;
|
||||||
|
/// Sets sub-assembly part and tag for a push tag.
|
||||||
|
void setPushTagSubIdAndTag(size_t _subId, size_t _tag);
|
||||||
|
|
||||||
AssemblyItemType type() const { return m_type; }
|
AssemblyItemType type() const { return m_type; }
|
||||||
u256 const& data() const { return m_data; }
|
u256 const& data() const { return m_data; }
|
||||||
|
@ -77,7 +77,6 @@ bool BlockDeduplicator::deduplicate()
|
|||||||
{
|
{
|
||||||
//@todo this should probably be optimized.
|
//@todo this should probably be optimized.
|
||||||
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
|
set<size_t, function<bool(size_t, size_t)>> blocksSeen(comparator);
|
||||||
map<u256, u256> tagReplacement;
|
|
||||||
for (size_t i = 0; i < m_items.size(); ++i)
|
for (size_t i = 0; i < m_items.size(); ++i)
|
||||||
{
|
{
|
||||||
if (m_items.at(i).type() != Tag)
|
if (m_items.at(i).type() != Tag)
|
||||||
@ -86,22 +85,40 @@ bool BlockDeduplicator::deduplicate()
|
|||||||
if (it == blocksSeen.end())
|
if (it == blocksSeen.end())
|
||||||
blocksSeen.insert(i);
|
blocksSeen.insert(i);
|
||||||
else
|
else
|
||||||
tagReplacement[m_items.at(i).data()] = m_items.at(*it).data();
|
m_replacedTags[m_items.at(i).data()] = m_items.at(*it).data();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool changed = false;
|
if (!applyTagReplacement(m_items, m_replacedTags))
|
||||||
for (AssemblyItem& item: m_items)
|
|
||||||
if (item.type() == PushTag && tagReplacement.count(item.data()))
|
|
||||||
{
|
|
||||||
changed = true;
|
|
||||||
item.setData(tagReplacement.at(item.data()));
|
|
||||||
}
|
|
||||||
if (!changed)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return iterations > 0;
|
return iterations > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BlockDeduplicator::applyTagReplacement(
|
||||||
|
AssemblyItems& _items,
|
||||||
|
map<u256, u256> const& _replacements,
|
||||||
|
size_t _subId
|
||||||
|
)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
for (AssemblyItem& item: _items)
|
||||||
|
if (item.type() == PushTag)
|
||||||
|
{
|
||||||
|
size_t subId;
|
||||||
|
size_t tagId;
|
||||||
|
tie(subId, tagId) = item.splitForeignPushTag();
|
||||||
|
if (subId != _subId)
|
||||||
|
continue;
|
||||||
|
auto it = _replacements.find(tagId);
|
||||||
|
if (it != _replacements.end())
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
item.setPushTagSubIdAndTag(subId, size_t(it->second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
|
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
|
||||||
{
|
{
|
||||||
if (it == end)
|
if (it == end)
|
||||||
|
@ -23,9 +23,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libdevcore/Common.h>
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -45,6 +48,18 @@ public:
|
|||||||
BlockDeduplicator(AssemblyItems& _items): m_items(_items) {}
|
BlockDeduplicator(AssemblyItems& _items): m_items(_items) {}
|
||||||
/// @returns true if something was changed
|
/// @returns true if something was changed
|
||||||
bool deduplicate();
|
bool deduplicate();
|
||||||
|
/// @returns the tags that were replaced.
|
||||||
|
std::map<u256, u256> const& replacedTags() const { return m_replacedTags; }
|
||||||
|
|
||||||
|
/// Replaces all PushTag operations insied @a _items that match a key in
|
||||||
|
/// @a _replacements by the respective value. If @a _subID is not -1, only
|
||||||
|
/// apply the replacement for foreign tags from this sub id.
|
||||||
|
/// @returns true iff a replacement was performed.
|
||||||
|
static bool applyTagReplacement(
|
||||||
|
AssemblyItems& _items,
|
||||||
|
std::map<u256, u256> const& _replacements,
|
||||||
|
size_t _subID = size_t(-1)
|
||||||
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Iterator that skips tags and skips to the end if (all branches of) the control
|
/// Iterator that skips tags and skips to the end if (all branches of) the control
|
||||||
@ -70,6 +85,7 @@ private:
|
|||||||
AssemblyItem const* replaceWith;
|
AssemblyItem const* replaceWith;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::map<u256, u256> m_replacedTags;
|
||||||
AssemblyItems& m_items;
|
AssemblyItems& m_items;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ file(GLOB HEADERS "*.h")
|
|||||||
|
|
||||||
include_directories(BEFORE ..)
|
include_directories(BEFORE ..)
|
||||||
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
|
add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS})
|
||||||
eth_use(${EXECUTABLE} REQUIRED Jsoncpp Dev::soldevcore)
|
eth_use(${EXECUTABLE} REQUIRED Dev::soldevcore)
|
||||||
|
target_link_libraries(${EXECUTABLE} jsoncpp)
|
||||||
|
|
||||||
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
|
install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
|
||||||
|
@ -89,6 +89,11 @@ struct BasicBlock
|
|||||||
|
|
||||||
using BasicBlocks = std::vector<BasicBlock>;
|
using BasicBlocks = std::vector<BasicBlock>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Control flow graph optimizer.
|
||||||
|
* ASSUMES THAT WE ONLY JUMP TO TAGS THAT WERE PREVIOUSLY PUSHED. THIS IS NOT TRUE ANYMORE
|
||||||
|
* NOW THAT FUNCTION TAGS CAN BE STORED IN STORAGE.
|
||||||
|
*/
|
||||||
class ControlFlowGraph
|
class ControlFlowGraph
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -39,7 +39,7 @@ GasMeter::GasConsumption& GasMeter::GasConsumption::operator+=(GasConsumption co
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
|
GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item, bool _includeExternalCosts)
|
||||||
{
|
{
|
||||||
GasConsumption gas;
|
GasConsumption gas;
|
||||||
switch (_item.type())
|
switch (_item.type())
|
||||||
@ -128,23 +128,35 @@ GasMeter::GasConsumption GasMeter::estimateMax(AssemblyItem const& _item)
|
|||||||
case Instruction::CALLCODE:
|
case Instruction::CALLCODE:
|
||||||
case Instruction::DELEGATECALL:
|
case Instruction::DELEGATECALL:
|
||||||
{
|
{
|
||||||
gas = GasCosts::callGas;
|
if (_includeExternalCosts)
|
||||||
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
|
// We assume that we do not know the target contract and thus, the consumption is infinite.
|
||||||
gas += (*value);
|
|
||||||
else
|
|
||||||
gas = GasConsumption::infinite();
|
gas = GasConsumption::infinite();
|
||||||
if (_item.instruction() == Instruction::CALL)
|
else
|
||||||
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
|
{
|
||||||
int valueSize = _item.instruction() == Instruction::DELEGATECALL ? 0 : 1;
|
gas = GasCosts::callGas;
|
||||||
if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize)))
|
if (u256 const* value = classes.knownConstant(m_state->relativeStackElement(0)))
|
||||||
gas += GasCosts::callValueTransferGas;
|
gas += (*value);
|
||||||
gas += memoryGas(-2 - valueSize, -3 - valueSize);
|
else
|
||||||
gas += memoryGas(-4 - valueSize, -5 - valueSize);
|
gas = GasConsumption::infinite();
|
||||||
|
if (_item.instruction() == Instruction::CALL)
|
||||||
|
gas += GasCosts::callNewAccountGas; // We very rarely know whether the address exists.
|
||||||
|
int valueSize = _item.instruction() == Instruction::DELEGATECALL ? 0 : 1;
|
||||||
|
if (!classes.knownZero(m_state->relativeStackElement(-1 - valueSize)))
|
||||||
|
gas += GasCosts::callValueTransferGas;
|
||||||
|
gas += memoryGas(-2 - valueSize, -3 - valueSize);
|
||||||
|
gas += memoryGas(-4 - valueSize, -5 - valueSize);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Instruction::CREATE:
|
case Instruction::CREATE:
|
||||||
gas = GasCosts::createGas;
|
if (_includeExternalCosts)
|
||||||
gas += memoryGas(-1, -2);
|
// We assume that we do not know the target contract and thus, the consumption is infinite.
|
||||||
|
gas = GasConsumption::infinite();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gas = GasCosts::createGas;
|
||||||
|
gas += memoryGas(-1, -2);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Instruction::EXP:
|
case Instruction::EXP:
|
||||||
gas = GasCosts::expGas;
|
gas = GasCosts::expGas;
|
||||||
|
@ -102,7 +102,8 @@ public:
|
|||||||
|
|
||||||
/// @returns an upper bound on the gas consumed by the given instruction and updates
|
/// @returns an upper bound on the gas consumed by the given instruction and updates
|
||||||
/// the state.
|
/// the state.
|
||||||
GasConsumption estimateMax(AssemblyItem const& _item);
|
/// @param _inculdeExternalCosts if true, include costs caused by other contracts in calls.
|
||||||
|
GasConsumption estimateMax(AssemblyItem const& _item, bool _includeExternalCosts = true);
|
||||||
|
|
||||||
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
|
u256 const& largestMemoryAccess() const { return m_largestMemoryAccess; }
|
||||||
|
|
||||||
|
148
libevmasm/PeepholeOptimiser.cpp
Normal file
148
libevmasm/PeepholeOptimiser.cpp
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file PeepholeOptimiser.h
|
||||||
|
* Performs local optimising code changes to assembly.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PeepholeOptimiser.h"
|
||||||
|
|
||||||
|
#include <libevmasm/AssemblyItem.h>
|
||||||
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace dev::eth;
|
||||||
|
using namespace dev;
|
||||||
|
|
||||||
|
// TODO: Extend this to use the tools from ExpressionClasses.cpp
|
||||||
|
|
||||||
|
struct Identity
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 1; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
|
||||||
|
{
|
||||||
|
*_out = *_in;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PushPop
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 2; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>)
|
||||||
|
{
|
||||||
|
auto t = _in[0].type();
|
||||||
|
if (_in[1] == Instruction::POP && (
|
||||||
|
SemanticInformation::isDupInstruction(_in[0]) ||
|
||||||
|
t == Push || t == PushString || t == PushTag || t == PushSub ||
|
||||||
|
t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress
|
||||||
|
))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DoubleSwap
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 2; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems>)
|
||||||
|
{
|
||||||
|
if (_in[0] == _in[1] && SemanticInformation::isSwapInstruction(_in[0]))
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct JumpToNext
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 3; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
_in[0].type() == PushTag &&
|
||||||
|
(_in[1] == Instruction::JUMP || _in[1] == Instruction::JUMPI) &&
|
||||||
|
_in[2].type() == Tag &&
|
||||||
|
_in[0].data() == _in[2].data()
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (_in[1] == Instruction::JUMPI)
|
||||||
|
*_out = AssemblyItem(Instruction::POP, _in[1].location());
|
||||||
|
*_out = _in[2];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TagConjunctions
|
||||||
|
{
|
||||||
|
static size_t windowSize() { return 3; }
|
||||||
|
static bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
_in[0].type() == PushTag &&
|
||||||
|
_in[2] == Instruction::AND &&
|
||||||
|
_in[1].type() == Push &&
|
||||||
|
(_in[1].data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
*_out = _in[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OptimiserState
|
||||||
|
{
|
||||||
|
AssemblyItems const& items;
|
||||||
|
size_t i;
|
||||||
|
std::back_insert_iterator<AssemblyItems> out;
|
||||||
|
};
|
||||||
|
|
||||||
|
void applyMethods(OptimiserState&)
|
||||||
|
{
|
||||||
|
assertThrow(false, OptimizerException, "Peephole optimizer failed to apply identity.");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Method, typename... OtherMethods>
|
||||||
|
void applyMethods(OptimiserState& _state, Method, OtherMethods... _other)
|
||||||
|
{
|
||||||
|
if (_state.i + Method::windowSize() <= _state.items.size() && Method::apply(_state.items.begin() + _state.i, _state.out))
|
||||||
|
_state.i += Method::windowSize();
|
||||||
|
else
|
||||||
|
applyMethods(_state, _other...);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PeepholeOptimiser::optimise()
|
||||||
|
{
|
||||||
|
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
|
||||||
|
while (state.i < m_items.size())
|
||||||
|
applyMethods(state, PushPop(), DoubleSwap(), JumpToNext(), TagConjunctions(), Identity());
|
||||||
|
if (m_optimisedItems.size() < m_items.size())
|
||||||
|
{
|
||||||
|
m_items = std::move(m_optimisedItems);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
54
libevmasm/PeepholeOptimiser.h
Normal file
54
libevmasm/PeepholeOptimiser.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file PeepholeOptimiser.h
|
||||||
|
* Performs local optimising code changes to assembly.
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace eth
|
||||||
|
{
|
||||||
|
class AssemblyItem;
|
||||||
|
using AssemblyItems = std::vector<AssemblyItem>;
|
||||||
|
|
||||||
|
class PeepholeOptimisationMethod
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual size_t windowSize() const;
|
||||||
|
virtual bool apply(AssemblyItems::const_iterator _in, std::back_insert_iterator<AssemblyItems> _out);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PeepholeOptimiser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit PeepholeOptimiser(AssemblyItems& _items): m_items(_items) {}
|
||||||
|
|
||||||
|
bool optimise();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AssemblyItems& m_items;
|
||||||
|
AssemblyItems m_optimisedItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -55,7 +55,11 @@ struct SourceLocation
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(SourceLocation const& _other) const { return start == _other.start && end == _other.end;}
|
bool operator==(SourceLocation const& _other) const
|
||||||
|
{
|
||||||
|
return start == _other.start && end == _other.end &&
|
||||||
|
((!sourceName && !_other.sourceName) || (sourceName && _other.sourceName && *sourceName == *_other.sourceName));
|
||||||
|
}
|
||||||
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
|
bool operator!=(SourceLocation const& _other) const { return !operator==(_other); }
|
||||||
inline bool operator<(SourceLocation const& _other) const;
|
inline bool operator<(SourceLocation const& _other) const;
|
||||||
inline bool contains(SourceLocation const& _other) const;
|
inline bool contains(SourceLocation const& _other) const;
|
||||||
@ -79,20 +83,21 @@ inline std::ostream& operator<<(std::ostream& _out, SourceLocation const& _locat
|
|||||||
bool SourceLocation::operator<(SourceLocation const& _other) const
|
bool SourceLocation::operator<(SourceLocation const& _other) const
|
||||||
{
|
{
|
||||||
if (!sourceName || !_other.sourceName)
|
if (!sourceName || !_other.sourceName)
|
||||||
return int(!!sourceName) < int(!!_other.sourceName);
|
return std::make_tuple(int(!!sourceName), start, end) < std::make_tuple(int(!!_other.sourceName), _other.start, _other.end);
|
||||||
return make_tuple(*sourceName, start, end) < make_tuple(*_other.sourceName, _other.start, _other.end);
|
else
|
||||||
|
return std::make_tuple(*sourceName, start, end) < std::make_tuple(*_other.sourceName, _other.start, _other.end);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SourceLocation::contains(SourceLocation const& _other) const
|
bool SourceLocation::contains(SourceLocation const& _other) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || _other.isEmpty() || !sourceName || !_other.sourceName || *sourceName != *_other.sourceName)
|
if (isEmpty() || _other.isEmpty() || ((!sourceName || !_other.sourceName || *sourceName != *_other.sourceName) && (sourceName || _other.sourceName)))
|
||||||
return false;
|
return false;
|
||||||
return start <= _other.start && _other.end <= end;
|
return start <= _other.start && _other.end <= end;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SourceLocation::intersects(SourceLocation const& _other) const
|
bool SourceLocation::intersects(SourceLocation const& _other) const
|
||||||
{
|
{
|
||||||
if (isEmpty() || _other.isEmpty() || !sourceName || !_other.sourceName || *sourceName != *_other.sourceName)
|
if (isEmpty() || _other.isEmpty() || ((!sourceName || !_other.sourceName || *sourceName != *_other.sourceName) && (sourceName || _other.sourceName)))
|
||||||
return false;
|
return false;
|
||||||
return _other.start < end && start < _other.end;
|
return _other.start < end && start < _other.end;
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,6 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB")
|
|||||||
|
|
||||||
aux_source_directory(. SRC_LIST)
|
aux_source_directory(. SRC_LIST)
|
||||||
|
|
||||||
#include_directories(BEFORE ${JSONCPP_INCLUDE_DIRS})
|
|
||||||
#include_directories(${Boost_INCLUDE_DIRS})
|
|
||||||
|
|
||||||
set(EXECUTABLE lll)
|
set(EXECUTABLE lll)
|
||||||
|
|
||||||
file(GLOB HEADERS "*.h")
|
file(GLOB HEADERS "*.h")
|
||||||
|
@ -35,9 +35,6 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
namespace qi = boost::spirit::qi;
|
|
||||||
namespace px = boost::phoenix;
|
|
||||||
namespace sp = boost::spirit;
|
|
||||||
|
|
||||||
void CodeFragment::finalise(CompilerState const& _cs)
|
void CodeFragment::finalise(CompilerState const& _cs)
|
||||||
{
|
{
|
||||||
@ -90,7 +87,7 @@ CodeFragment::CodeFragment(sp::utree const& _t, CompilerState& _s, bool _allowAS
|
|||||||
m_asm.append(_s.args.at(s).m_asm);
|
m_asm.append(_s.args.at(s).m_asm);
|
||||||
else if (_s.outers.count(s))
|
else if (_s.outers.count(s))
|
||||||
m_asm.append(_s.outers.at(s).m_asm);
|
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)
|
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_-") == string::npos)
|
||||||
{
|
{
|
||||||
auto it = _s.vars.find(s);
|
auto it = _s.vars.find(s);
|
||||||
if (it == _s.vars.end())
|
if (it == _s.vars.end())
|
||||||
@ -477,7 +474,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|||||||
requireSize(2);
|
requireSize(2);
|
||||||
requireDeposit(0, 1);
|
requireDeposit(0, 1);
|
||||||
|
|
||||||
auto begin = m_asm.append();
|
auto begin = m_asm.append(m_asm.newTag());
|
||||||
m_asm.append(code[0].m_asm);
|
m_asm.append(code[0].m_asm);
|
||||||
if (us == "WHILE")
|
if (us == "WHILE")
|
||||||
m_asm.append(Instruction::ISZERO);
|
m_asm.append(Instruction::ISZERO);
|
||||||
@ -492,7 +489,7 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|||||||
requireDeposit(1, 1);
|
requireDeposit(1, 1);
|
||||||
|
|
||||||
m_asm.append(code[0].m_asm, 0);
|
m_asm.append(code[0].m_asm, 0);
|
||||||
auto begin = m_asm.append();
|
auto begin = m_asm.append(m_asm.newTag());
|
||||||
m_asm.append(code[1].m_asm);
|
m_asm.append(code[1].m_asm);
|
||||||
m_asm.append(Instruction::ISZERO);
|
m_asm.append(Instruction::ISZERO);
|
||||||
auto end = m_asm.appendJumpI();
|
auto end = m_asm.appendJumpI();
|
||||||
@ -523,7 +520,8 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|||||||
requireMaxSize(3);
|
requireMaxSize(3);
|
||||||
requireDeposit(1, 1);
|
requireDeposit(1, 1);
|
||||||
|
|
||||||
auto subPush = m_asm.appendSubSize(code[0].assembly(ns));
|
auto subPush = m_asm.newSub(make_shared<Assembly>(code[0].assembly(ns)));
|
||||||
|
m_asm.append(m_asm.newPushSubSize(subPush.data()));
|
||||||
m_asm.append(Instruction::DUP1);
|
m_asm.append(Instruction::DUP1);
|
||||||
if (code.size() == 3)
|
if (code.size() == 3)
|
||||||
{
|
{
|
||||||
@ -584,10 +582,14 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
|
|||||||
{
|
{
|
||||||
m_asm.appendJump(m_asm.errorTag());
|
m_asm.appendJump(m_asm.errorTag());
|
||||||
}
|
}
|
||||||
else if (us.find_first_of("1234567890") != 0 && us.find_first_not_of("QWERTYUIOPASDFGHJKLZXCVBNM1234567890_") == string::npos)
|
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));
|
m_asm.append((u256)varAddress(s));
|
||||||
else
|
else
|
||||||
error<InvalidOperation>();
|
error<InvalidOperation>("Unsupported keyword: '" + us + "'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,11 @@ private:
|
|||||||
void finalise(CompilerState const& _cs);
|
void finalise(CompilerState const& _cs);
|
||||||
|
|
||||||
template <class T> void error() const { BOOST_THROW_EXCEPTION(T() ); }
|
template <class T> void error() const { BOOST_THROW_EXCEPTION(T() ); }
|
||||||
|
template <class T> void error(std::string const& reason) const {
|
||||||
|
auto err = T();
|
||||||
|
err << errinfo_comment(reason);
|
||||||
|
BOOST_THROW_EXCEPTION(err);
|
||||||
|
}
|
||||||
void constructOperation(sp::utree const& _t, CompilerState& _s);
|
void constructOperation(sp::utree const& _t, CompilerState& _s);
|
||||||
|
|
||||||
bool m_finalised = false;
|
bool m_finalised = false;
|
||||||
|
@ -34,8 +34,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error
|
|||||||
{
|
{
|
||||||
CompilerState cs;
|
CompilerState cs;
|
||||||
cs.populateStandard();
|
cs.populateStandard();
|
||||||
auto f = CodeFragment::compile(_src, cs);
|
bytes ret = CodeFragment::compile(_src, cs).assembly(cs).optimise(_opt).assemble().bytecode;
|
||||||
bytes ret = f.assembly(cs).optimise(_opt).assemble().bytecode;
|
|
||||||
for (auto i: cs.treesToKill)
|
for (auto i: cs.treesToKill)
|
||||||
killBigints(i);
|
killBigints(i);
|
||||||
return ret;
|
return ret;
|
||||||
@ -59,7 +58,7 @@ bytes dev::eth::compileLLL(string const& _src, bool _opt, vector<string>* _error
|
|||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
_errors->push_back("Internal parse exception.");
|
_errors->push_back("Internal compiler exception.");
|
||||||
}
|
}
|
||||||
return bytes();
|
return bytes();
|
||||||
}
|
}
|
||||||
@ -93,7 +92,7 @@ std::string dev::eth::compileLLLToAsm(std::string const& _src, bool _opt, std::v
|
|||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
if (_errors)
|
if (_errors)
|
||||||
_errors->push_back("Internal parse exception.");
|
_errors->push_back("Internal compiler exception.");
|
||||||
}
|
}
|
||||||
return string();
|
return string();
|
||||||
}
|
}
|
||||||
|
@ -101,8 +101,8 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out)
|
|||||||
qi::rule<it, string()> strsh = '\'' > qi::lexeme[+(~qi::char_(std::string(" ;$@()[]{}:\n\t") + '\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, 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, 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, bigint()> integer = intstr;
|
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 = px::construct<sp::any_ptr>(px::new_<bigint>(qi::_1))] | (str | strsh)[qi::_val = qi::_1] | symbol[qi::_val = 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()> seq = '{' > *element > '}';
|
||||||
qi::rule<it, space_type, sp::utree::list_type()> mload = '@' > 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()> sload = qi::lit("@@") > element;
|
||||||
@ -135,10 +135,19 @@ void dev::eth::parseTreeLLL(string const& _s, sp::utree& o_out)
|
|||||||
s.push_back(i);
|
s.push_back(i);
|
||||||
}
|
}
|
||||||
auto ret = s.cbegin();
|
auto ret = s.cbegin();
|
||||||
qi::phrase_parse(ret, s.cend(), element, space, qi::skip_flag::dont_postskip, o_out);
|
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 = std::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)
|
for (auto i = ret; i != s.cend(); ++i)
|
||||||
if (!isspace(*i)) {
|
if (!isspace(*i)) {
|
||||||
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser"));
|
BOOST_THROW_EXCEPTION(ParserException() << errinfo_comment("Non-whitespace left in parser"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,6 +83,31 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
|
|||||||
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
|
||||||
|
{
|
||||||
|
switch (_typeName.visibility())
|
||||||
|
{
|
||||||
|
case VariableDeclaration::Visibility::Default:
|
||||||
|
case VariableDeclaration::Visibility::Internal:
|
||||||
|
case VariableDeclaration::Visibility::External:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
|
||||||
|
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
|
||||||
|
if (_typeName.visibility() == VariableDeclaration::Visibility::External)
|
||||||
|
for (auto const& t: _typeName.parameterTypes() + _typeName.returnParameterTypes())
|
||||||
|
{
|
||||||
|
solAssert(t->annotation().type, "Type not set for parameter.");
|
||||||
|
if (!t->annotation().type->canBeUsedExternally(false))
|
||||||
|
fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_typeName.annotation().type = make_shared<FunctionType>(_typeName);
|
||||||
|
}
|
||||||
|
|
||||||
void ReferencesResolver::endVisit(Mapping const& _typeName)
|
void ReferencesResolver::endVisit(Mapping const& _typeName)
|
||||||
{
|
{
|
||||||
TypePointer keyType = _typeName.keyType().annotation().type;
|
TypePointer keyType = _typeName.keyType().annotation().type;
|
||||||
|
@ -62,6 +62,7 @@ private:
|
|||||||
virtual bool visit(Identifier const& _identifier) override;
|
virtual bool visit(Identifier const& _identifier) override;
|
||||||
virtual bool visit(ElementaryTypeName const& _typeName) override;
|
virtual bool visit(ElementaryTypeName const& _typeName) override;
|
||||||
virtual void endVisit(UserDefinedTypeName const& _typeName) override;
|
virtual void endVisit(UserDefinedTypeName const& _typeName) override;
|
||||||
|
virtual void endVisit(FunctionTypeName const& _typeName) override;
|
||||||
virtual void endVisit(Mapping const& _typeName) override;
|
virtual void endVisit(Mapping const& _typeName) override;
|
||||||
virtual void endVisit(ArrayTypeName const& _typeName) override;
|
virtual void endVisit(ArrayTypeName const& _typeName) override;
|
||||||
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
|
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
|
@ -574,6 +574,14 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TypeChecker::endVisit(FunctionTypeName const& _funType)
|
||||||
|
{
|
||||||
|
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
|
||||||
|
if (fun.location() == FunctionType::Location::External)
|
||||||
|
if (!fun.canBeUsedExternally(false))
|
||||||
|
typeError(_funType.location(), "External function type uses internal types.");
|
||||||
|
}
|
||||||
|
|
||||||
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||||
{
|
{
|
||||||
// Inline assembly does not have its own type-checking phase, so we just run the
|
// Inline assembly does not have its own type-checking phase, so we just run the
|
||||||
@ -996,9 +1004,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
|||||||
fatalTypeError(components[i]->location(), "Invalid mobile type.");
|
fatalTypeError(components[i]->location(), "Invalid mobile type.");
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
inlineArrayType = types[i]->mobileType();
|
inlineArrayType = types[i];
|
||||||
else if (inlineArrayType)
|
else if (inlineArrayType)
|
||||||
inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType());
|
inlineArrayType = Type::commonType(inlineArrayType, types[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -87,6 +87,7 @@ private:
|
|||||||
/// case this is a base constructor call.
|
/// case this is a base constructor call.
|
||||||
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
|
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
|
||||||
virtual bool visit(EventDefinition const& _eventDef) override;
|
virtual bool visit(EventDefinition const& _eventDef) override;
|
||||||
|
virtual void endVisit(FunctionTypeName const& _funType) override;
|
||||||
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
|
virtual bool visit(InlineAssembly const& _inlineAssembly) override;
|
||||||
virtual bool visit(IfStatement const& _ifStatement) override;
|
virtual bool visit(IfStatement const& _ifStatement) override;
|
||||||
virtual bool visit(WhileStatement const& _whileStatement) override;
|
virtual bool visit(WhileStatement const& _whileStatement) override;
|
||||||
|
@ -160,22 +160,22 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
|
|||||||
return *m_interfaceFunctionList;
|
return *m_interfaceFunctionList;
|
||||||
}
|
}
|
||||||
|
|
||||||
string const& ContractDefinition::devDocumentation() const
|
Json::Value const& ContractDefinition::devDocumentation() const
|
||||||
{
|
{
|
||||||
return m_devDocumentation;
|
return m_devDocumentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
string const& ContractDefinition::userDocumentation() const
|
Json::Value const& ContractDefinition::userDocumentation() const
|
||||||
{
|
{
|
||||||
return m_userDocumentation;
|
return m_userDocumentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractDefinition::setDevDocumentation(string const& _devDocumentation)
|
void ContractDefinition::setDevDocumentation(Json::Value const& _devDocumentation)
|
||||||
{
|
{
|
||||||
m_devDocumentation = _devDocumentation;
|
m_devDocumentation = _devDocumentation;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractDefinition::setUserDocumentation(string const& _userDocumentation)
|
void ContractDefinition::setUserDocumentation(Json::Value const& _userDocumentation)
|
||||||
{
|
{
|
||||||
m_userDocumentation = _userDocumentation;
|
m_userDocumentation = _userDocumentation;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include <libsolidity/ast/Types.h>
|
#include <libsolidity/ast/Types.h>
|
||||||
#include <libsolidity/interface/Exceptions.h>
|
#include <libsolidity/interface/Exceptions.h>
|
||||||
#include <libsolidity/ast/ASTAnnotations.h>
|
#include <libsolidity/ast/ASTAnnotations.h>
|
||||||
|
#include <json/json.h>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
@ -343,11 +344,11 @@ public:
|
|||||||
/// Returns the fallback function or nullptr if no fallback function was specified.
|
/// Returns the fallback function or nullptr if no fallback function was specified.
|
||||||
FunctionDefinition const* fallbackFunction() const;
|
FunctionDefinition const* fallbackFunction() const;
|
||||||
|
|
||||||
std::string const& userDocumentation() const;
|
Json::Value const& userDocumentation() const;
|
||||||
void setUserDocumentation(std::string const& _userDocumentation);
|
void setUserDocumentation(Json::Value const& _userDocumentation);
|
||||||
|
|
||||||
std::string const& devDocumentation() const;
|
Json::Value const& devDocumentation() const;
|
||||||
void setDevDocumentation(std::string const& _devDocumentation);
|
void setDevDocumentation(Json::Value const& _devDocumentation);
|
||||||
|
|
||||||
virtual TypePointer type() const override;
|
virtual TypePointer type() const override;
|
||||||
|
|
||||||
@ -359,8 +360,8 @@ private:
|
|||||||
bool m_isLibrary;
|
bool m_isLibrary;
|
||||||
|
|
||||||
// parsed Natspec documentation of the contract.
|
// parsed Natspec documentation of the contract.
|
||||||
std::string m_userDocumentation;
|
Json::Value m_userDocumentation;
|
||||||
std::string m_devDocumentation;
|
Json::Value m_devDocumentation;
|
||||||
|
|
||||||
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
|
std::vector<ContractDefinition const*> m_linearizedBaseContracts;
|
||||||
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
|
mutable std::unique_ptr<std::vector<std::pair<FixedHash<4>, FunctionTypePointer>>> m_interfaceFunctionList;
|
||||||
@ -821,6 +822,41 @@ private:
|
|||||||
std::vector<ASTString> m_namePath;
|
std::vector<ASTString> m_namePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A literal function type. Its source form is "function (paramType1, paramType2) internal / external returns (retType1, retType2)"
|
||||||
|
*/
|
||||||
|
class FunctionTypeName: public TypeName
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FunctionTypeName(
|
||||||
|
SourceLocation const& _location,
|
||||||
|
ASTPointer<ParameterList> const& _parameterTypes,
|
||||||
|
ASTPointer<ParameterList> const& _returnTypes,
|
||||||
|
Declaration::Visibility _visibility,
|
||||||
|
bool _isDeclaredConst,
|
||||||
|
bool _isPayable
|
||||||
|
):
|
||||||
|
TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes),
|
||||||
|
m_visibility(_visibility), m_isDeclaredConst(_isDeclaredConst), m_isPayable(_isPayable)
|
||||||
|
{}
|
||||||
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
|
std::vector<ASTPointer<VariableDeclaration>> const& parameterTypes() const { return m_parameterTypes->parameters(); }
|
||||||
|
std::vector<ASTPointer<VariableDeclaration>> const& returnParameterTypes() const { return m_returnTypes->parameters(); }
|
||||||
|
|
||||||
|
Declaration::Visibility visibility() const { return m_visibility; }
|
||||||
|
bool isDeclaredConst() const { return m_isDeclaredConst; }
|
||||||
|
bool isPayable() const { return m_isPayable; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
ASTPointer<ParameterList> m_parameterTypes;
|
||||||
|
ASTPointer<ParameterList> m_returnTypes;
|
||||||
|
Declaration::Visibility m_visibility;
|
||||||
|
bool m_isDeclaredConst;
|
||||||
|
bool m_isPayable;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping type. Its source form is "mapping('keyType' => 'valueType')"
|
* A mapping type. Its source form is "mapping('keyType' => 'valueType')"
|
||||||
*/
|
*/
|
||||||
@ -1005,18 +1041,22 @@ public:
|
|||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
ASTPointer<ASTString> const& _docString,
|
ASTPointer<ASTString> const& _docString,
|
||||||
ASTPointer<Expression> const& _condition,
|
ASTPointer<Expression> const& _condition,
|
||||||
ASTPointer<Statement> const& _body
|
ASTPointer<Statement> const& _body,
|
||||||
|
bool _isDoWhile
|
||||||
):
|
):
|
||||||
BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body) {}
|
BreakableStatement(_location, _docString), m_condition(_condition), m_body(_body),
|
||||||
|
m_isDoWhile(_isDoWhile) {}
|
||||||
virtual void accept(ASTVisitor& _visitor) override;
|
virtual void accept(ASTVisitor& _visitor) override;
|
||||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||||
|
|
||||||
Expression const& condition() const { return *m_condition; }
|
Expression const& condition() const { return *m_condition; }
|
||||||
Statement const& body() const { return *m_body; }
|
Statement const& body() const { return *m_body; }
|
||||||
|
bool isDoWhile() const { return m_isDoWhile; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<Expression> m_condition;
|
ASTPointer<Expression> m_condition;
|
||||||
ASTPointer<Statement> m_body;
|
ASTPointer<Statement> m_body;
|
||||||
|
bool m_isDoWhile;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,6 +54,7 @@ class MagicVariableDeclaration;
|
|||||||
class TypeName;
|
class TypeName;
|
||||||
class ElementaryTypeName;
|
class ElementaryTypeName;
|
||||||
class UserDefinedTypeName;
|
class UserDefinedTypeName;
|
||||||
|
class FunctionTypeName;
|
||||||
class Mapping;
|
class Mapping;
|
||||||
class ArrayTypeName;
|
class ArrayTypeName;
|
||||||
class Statement;
|
class Statement;
|
||||||
|
@ -174,7 +174,8 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
|||||||
addJsonNode(_node, "FunctionDefinition", {
|
addJsonNode(_node, "FunctionDefinition", {
|
||||||
make_pair("name", _node.name()),
|
make_pair("name", _node.name()),
|
||||||
make_pair("public", _node.isPublic()),
|
make_pair("public", _node.isPublic()),
|
||||||
make_pair("constant", _node.isDeclaredConst())
|
make_pair("constant", _node.isDeclaredConst()),
|
||||||
|
make_pair("payable", _node.isPayable())
|
||||||
}, true);
|
}, true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -225,6 +226,20 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTJsonConverter::visit(FunctionTypeName const& _node)
|
||||||
|
{
|
||||||
|
string visibility = "internal";
|
||||||
|
if (_node.visibility() == Declaration::Visibility::External)
|
||||||
|
visibility = "external";
|
||||||
|
|
||||||
|
addJsonNode(_node, "FunctionTypeName", {
|
||||||
|
make_pair("payable", _node.isPayable()),
|
||||||
|
make_pair("visibility", visibility),
|
||||||
|
make_pair("constant", _node.isDeclaredConst())
|
||||||
|
}, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTJsonConverter::visit(Mapping const& _node)
|
bool ASTJsonConverter::visit(Mapping const& _node)
|
||||||
{
|
{
|
||||||
addJsonNode(_node, "Mapping", {}, true);
|
addJsonNode(_node, "Mapping", {}, true);
|
||||||
@ -263,7 +278,11 @@ bool ASTJsonConverter::visit(IfStatement const& _node)
|
|||||||
|
|
||||||
bool ASTJsonConverter::visit(WhileStatement const& _node)
|
bool ASTJsonConverter::visit(WhileStatement const& _node)
|
||||||
{
|
{
|
||||||
addJsonNode(_node, "WhileStatement", {}, true);
|
addJsonNode(
|
||||||
|
_node,
|
||||||
|
_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement",
|
||||||
|
{},
|
||||||
|
true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,6 +521,11 @@ void ASTJsonConverter::endVisit(UserDefinedTypeName const&)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTJsonConverter::endVisit(FunctionTypeName const&)
|
||||||
|
{
|
||||||
|
goUp();
|
||||||
|
}
|
||||||
|
|
||||||
void ASTJsonConverter::endVisit(Mapping const&)
|
void ASTJsonConverter::endVisit(Mapping const&)
|
||||||
{
|
{
|
||||||
goUp();
|
goUp();
|
||||||
|
@ -69,6 +69,7 @@ public:
|
|||||||
bool visit(TypeName const& _node) override;
|
bool visit(TypeName const& _node) override;
|
||||||
bool visit(ElementaryTypeName const& _node) override;
|
bool visit(ElementaryTypeName const& _node) override;
|
||||||
bool visit(UserDefinedTypeName const& _node) override;
|
bool visit(UserDefinedTypeName const& _node) override;
|
||||||
|
bool visit(FunctionTypeName const& _node) override;
|
||||||
bool visit(Mapping const& _node) override;
|
bool visit(Mapping const& _node) override;
|
||||||
bool visit(ArrayTypeName const& _node) override;
|
bool visit(ArrayTypeName const& _node) override;
|
||||||
bool visit(InlineAssembly const& _node) override;
|
bool visit(InlineAssembly const& _node) override;
|
||||||
@ -114,6 +115,7 @@ public:
|
|||||||
void endVisit(TypeName const&) override;
|
void endVisit(TypeName const&) override;
|
||||||
void endVisit(ElementaryTypeName const&) override;
|
void endVisit(ElementaryTypeName const&) override;
|
||||||
void endVisit(UserDefinedTypeName const&) override;
|
void endVisit(UserDefinedTypeName const&) override;
|
||||||
|
void endVisit(FunctionTypeName const&) override;
|
||||||
void endVisit(Mapping const&) override;
|
void endVisit(Mapping const&) override;
|
||||||
void endVisit(ArrayTypeName const&) override;
|
void endVisit(ArrayTypeName const&) override;
|
||||||
void endVisit(InlineAssembly const&) override;
|
void endVisit(InlineAssembly const&) override;
|
||||||
|
@ -164,6 +164,13 @@ bool ASTPrinter::visit(UserDefinedTypeName const& _node)
|
|||||||
return goDeeper();
|
return goDeeper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ASTPrinter::visit(FunctionTypeName const& _node)
|
||||||
|
{
|
||||||
|
writeLine("FunctionTypeName");
|
||||||
|
printSourcePart(_node);
|
||||||
|
return goDeeper();
|
||||||
|
}
|
||||||
|
|
||||||
bool ASTPrinter::visit(Mapping const& _node)
|
bool ASTPrinter::visit(Mapping const& _node)
|
||||||
{
|
{
|
||||||
writeLine("Mapping");
|
writeLine("Mapping");
|
||||||
@ -208,7 +215,7 @@ bool ASTPrinter::visit(IfStatement const& _node)
|
|||||||
|
|
||||||
bool ASTPrinter::visit(WhileStatement const& _node)
|
bool ASTPrinter::visit(WhileStatement const& _node)
|
||||||
{
|
{
|
||||||
writeLine("WhileStatement");
|
writeLine(_node.isDoWhile() ? "DoWhileStatement" : "WhileStatement");
|
||||||
printSourcePart(_node);
|
printSourcePart(_node);
|
||||||
return goDeeper();
|
return goDeeper();
|
||||||
}
|
}
|
||||||
@ -442,6 +449,11 @@ void ASTPrinter::endVisit(UserDefinedTypeName const&)
|
|||||||
m_indentation--;
|
m_indentation--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ASTPrinter::endVisit(FunctionTypeName const&)
|
||||||
|
{
|
||||||
|
m_indentation--;
|
||||||
|
}
|
||||||
|
|
||||||
void ASTPrinter::endVisit(Mapping const&)
|
void ASTPrinter::endVisit(Mapping const&)
|
||||||
{
|
{
|
||||||
m_indentation--;
|
m_indentation--;
|
||||||
|
@ -63,6 +63,7 @@ public:
|
|||||||
bool visit(TypeName const& _node) override;
|
bool visit(TypeName const& _node) override;
|
||||||
bool visit(ElementaryTypeName const& _node) override;
|
bool visit(ElementaryTypeName const& _node) override;
|
||||||
bool visit(UserDefinedTypeName const& _node) override;
|
bool visit(UserDefinedTypeName const& _node) override;
|
||||||
|
bool visit(FunctionTypeName const& _node) override;
|
||||||
bool visit(Mapping const& _node) override;
|
bool visit(Mapping const& _node) override;
|
||||||
bool visit(ArrayTypeName const& _node) override;
|
bool visit(ArrayTypeName const& _node) override;
|
||||||
bool visit(InlineAssembly const& _node) override;
|
bool visit(InlineAssembly const& _node) override;
|
||||||
@ -106,6 +107,7 @@ public:
|
|||||||
void endVisit(TypeName const&) override;
|
void endVisit(TypeName const&) override;
|
||||||
void endVisit(ElementaryTypeName const&) override;
|
void endVisit(ElementaryTypeName const&) override;
|
||||||
void endVisit(UserDefinedTypeName const&) override;
|
void endVisit(UserDefinedTypeName const&) override;
|
||||||
|
void endVisit(FunctionTypeName const&) override;
|
||||||
void endVisit(Mapping const&) override;
|
void endVisit(Mapping const&) override;
|
||||||
void endVisit(ArrayTypeName const&) override;
|
void endVisit(ArrayTypeName const&) override;
|
||||||
void endVisit(InlineAssembly const&) override;
|
void endVisit(InlineAssembly const&) override;
|
||||||
|
@ -61,6 +61,7 @@ public:
|
|||||||
virtual bool visit(TypeName& _node) { return visitNode(_node); }
|
virtual bool visit(TypeName& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); }
|
virtual bool visit(ElementaryTypeName& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); }
|
virtual bool visit(UserDefinedTypeName& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(FunctionTypeName& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Mapping& _node) { return visitNode(_node); }
|
virtual bool visit(Mapping& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); }
|
virtual bool visit(ArrayTypeName& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(InlineAssembly& _node) { return visitNode(_node); }
|
virtual bool visit(InlineAssembly& _node) { return visitNode(_node); }
|
||||||
@ -106,6 +107,7 @@ public:
|
|||||||
virtual void endVisit(TypeName& _node) { endVisitNode(_node); }
|
virtual void endVisit(TypeName& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); }
|
virtual void endVisit(ElementaryTypeName& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); }
|
virtual void endVisit(UserDefinedTypeName& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(FunctionTypeName& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Mapping& _node) { endVisitNode(_node); }
|
virtual void endVisit(Mapping& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); }
|
virtual void endVisit(ArrayTypeName& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(InlineAssembly& _node) { endVisitNode(_node); }
|
virtual void endVisit(InlineAssembly& _node) { endVisitNode(_node); }
|
||||||
@ -163,6 +165,7 @@ public:
|
|||||||
virtual bool visit(TypeName const& _node) { return visitNode(_node); }
|
virtual bool visit(TypeName const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); }
|
virtual bool visit(ElementaryTypeName const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); }
|
virtual bool visit(UserDefinedTypeName const& _node) { return visitNode(_node); }
|
||||||
|
virtual bool visit(FunctionTypeName const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Mapping const& _node) { return visitNode(_node); }
|
virtual bool visit(Mapping const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(ArrayTypeName const& _node) { return visitNode(_node); }
|
virtual bool visit(ArrayTypeName const& _node) { return visitNode(_node); }
|
||||||
virtual bool visit(Block const& _node) { return visitNode(_node); }
|
virtual bool visit(Block const& _node) { return visitNode(_node); }
|
||||||
@ -208,6 +211,7 @@ public:
|
|||||||
virtual void endVisit(TypeName const& _node) { endVisitNode(_node); }
|
virtual void endVisit(TypeName const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); }
|
virtual void endVisit(ElementaryTypeName const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); }
|
virtual void endVisit(UserDefinedTypeName const& _node) { endVisitNode(_node); }
|
||||||
|
virtual void endVisit(FunctionTypeName const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Mapping const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Mapping const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(ArrayTypeName const& _node) { endVisitNode(_node); }
|
virtual void endVisit(ArrayTypeName const& _node) { endVisitNode(_node); }
|
||||||
virtual void endVisit(Block const& _node) { endVisitNode(_node); }
|
virtual void endVisit(Block const& _node) { endVisitNode(_node); }
|
||||||
|
@ -327,6 +327,26 @@ void UserDefinedTypeName::accept(ASTConstVisitor& _visitor) const
|
|||||||
_visitor.endVisit(*this);
|
_visitor.endVisit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FunctionTypeName::accept(ASTVisitor& _visitor)
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
{
|
||||||
|
m_parameterTypes->accept(_visitor);
|
||||||
|
m_returnTypes->accept(_visitor);
|
||||||
|
}
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FunctionTypeName::accept(ASTConstVisitor& _visitor) const
|
||||||
|
{
|
||||||
|
if (_visitor.visit(*this))
|
||||||
|
{
|
||||||
|
m_parameterTypes->accept(_visitor);
|
||||||
|
m_returnTypes->accept(_visitor);
|
||||||
|
}
|
||||||
|
_visitor.endVisit(*this);
|
||||||
|
}
|
||||||
|
|
||||||
void Mapping::accept(ASTVisitor& _visitor)
|
void Mapping::accept(ASTVisitor& _visitor)
|
||||||
{
|
{
|
||||||
if (_visitor.visit(*this))
|
if (_visitor.visit(*this))
|
||||||
|
@ -200,10 +200,10 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
|
|||||||
{
|
{
|
||||||
if (!_a || !_b)
|
if (!_a || !_b)
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
else if (_b->isImplicitlyConvertibleTo(*_a))
|
else if (_b->isImplicitlyConvertibleTo(*_a->mobileType()))
|
||||||
return _a;
|
return _a->mobileType();
|
||||||
else if (_a->isImplicitlyConvertibleTo(*_b))
|
else if (_a->isImplicitlyConvertibleTo(*_b->mobileType()))
|
||||||
return _b;
|
return _b->mobileType();
|
||||||
else
|
else
|
||||||
return TypePointer();
|
return TypePointer();
|
||||||
}
|
}
|
||||||
@ -1265,6 +1265,7 @@ TypePointer ArrayType::decodingType() const
|
|||||||
|
|
||||||
TypePointer ArrayType::interfaceType(bool _inLibrary) const
|
TypePointer ArrayType::interfaceType(bool _inLibrary) const
|
||||||
{
|
{
|
||||||
|
// Note: This has to fulfill canBeUsedExternally(_inLibrary) == !!interfaceType(_inLibrary)
|
||||||
if (_inLibrary && location() == DataLocation::Storage)
|
if (_inLibrary && location() == DataLocation::Storage)
|
||||||
return shared_from_this();
|
return shared_from_this();
|
||||||
|
|
||||||
@ -1282,6 +1283,21 @@ TypePointer ArrayType::interfaceType(bool _inLibrary) const
|
|||||||
return make_shared<ArrayType>(DataLocation::Memory, baseExt, m_length);
|
return make_shared<ArrayType>(DataLocation::Memory, baseExt, m_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ArrayType::canBeUsedExternally(bool _inLibrary) const
|
||||||
|
{
|
||||||
|
// Note: This has to fulfill canBeUsedExternally(_inLibrary) == !!interfaceType(_inLibrary)
|
||||||
|
if (_inLibrary && location() == DataLocation::Storage)
|
||||||
|
return true;
|
||||||
|
else if (m_arrayKind != ArrayKind::Ordinary)
|
||||||
|
return true;
|
||||||
|
else if (!m_baseType->canBeUsedExternally(_inLibrary))
|
||||||
|
return false;
|
||||||
|
else if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
u256 ArrayType::memorySize() const
|
u256 ArrayType::memorySize() const
|
||||||
{
|
{
|
||||||
solAssert(!isDynamicallySized(), "");
|
solAssert(!isDynamicallySized(), "");
|
||||||
@ -1451,7 +1467,7 @@ u256 StructType::storageSize() const
|
|||||||
|
|
||||||
string StructType::toString(bool _short) const
|
string StructType::toString(bool _short) const
|
||||||
{
|
{
|
||||||
string ret = "struct " + m_struct.name();
|
string ret = "struct " + m_struct.annotation().canonicalName;
|
||||||
if (!_short)
|
if (!_short)
|
||||||
ret += " " + stringForReferencePart();
|
ret += " " + stringForReferencePart();
|
||||||
return ret;
|
return ret;
|
||||||
@ -1561,7 +1577,7 @@ bool EnumType::operator==(Type const& _other) const
|
|||||||
|
|
||||||
unsigned EnumType::storageBytes() const
|
unsigned EnumType::storageBytes() const
|
||||||
{
|
{
|
||||||
size_t elements = m_enum.members().size();
|
size_t elements = numberOfMembers();
|
||||||
if (elements <= 1)
|
if (elements <= 1)
|
||||||
return 1;
|
return 1;
|
||||||
else
|
else
|
||||||
@ -1570,7 +1586,7 @@ unsigned EnumType::storageBytes() const
|
|||||||
|
|
||||||
string EnumType::toString(bool) const
|
string EnumType::toString(bool) const
|
||||||
{
|
{
|
||||||
return string("enum ") + m_enum.name();
|
return string("enum ") + m_enum.annotation().canonicalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
string EnumType::canonicalName(bool) const
|
string EnumType::canonicalName(bool) const
|
||||||
@ -1578,9 +1594,14 @@ string EnumType::canonicalName(bool) const
|
|||||||
return m_enum.annotation().canonicalName;
|
return m_enum.annotation().canonicalName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t EnumType::numberOfMembers() const
|
||||||
|
{
|
||||||
|
return m_enum.members().size();
|
||||||
|
};
|
||||||
|
|
||||||
bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
bool EnumType::isExplicitlyConvertibleTo(Type const& _convertTo) const
|
||||||
{
|
{
|
||||||
return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
|
return _convertTo == *this || _convertTo.category() == Category::Integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned EnumType::memberValue(ASTString const& _member) const
|
unsigned EnumType::memberValue(ASTString const& _member) const
|
||||||
@ -1699,7 +1720,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
|
|||||||
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
|
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
|
||||||
m_location(_isInternal ? Location::Internal : Location::External),
|
m_location(_isInternal ? Location::Internal : Location::External),
|
||||||
m_isConstant(_function.isDeclaredConst()),
|
m_isConstant(_function.isDeclaredConst()),
|
||||||
m_isPayable(_function.isPayable()),
|
m_isPayable(_isInternal ? false : _function.isPayable()),
|
||||||
m_declaration(&_function)
|
m_declaration(&_function)
|
||||||
{
|
{
|
||||||
TypePointers params;
|
TypePointers params;
|
||||||
@ -1800,6 +1821,38 @@ FunctionType::FunctionType(EventDefinition const& _event):
|
|||||||
swap(paramNames, m_parameterNames);
|
swap(paramNames, m_parameterNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionType::FunctionType(FunctionTypeName const& _typeName):
|
||||||
|
m_location(_typeName.visibility() == VariableDeclaration::Visibility::External ? Location::External : Location::Internal),
|
||||||
|
m_isConstant(_typeName.isDeclaredConst()),
|
||||||
|
m_isPayable(_typeName.isPayable())
|
||||||
|
{
|
||||||
|
if (_typeName.isPayable())
|
||||||
|
{
|
||||||
|
solAssert(m_location == Location::External, "Internal payable function type used.");
|
||||||
|
solAssert(!m_isConstant, "Payable constant function");
|
||||||
|
}
|
||||||
|
for (auto const& t: _typeName.parameterTypes())
|
||||||
|
{
|
||||||
|
solAssert(t->annotation().type, "Type not set for parameter.");
|
||||||
|
if (m_location == Location::External)
|
||||||
|
solAssert(
|
||||||
|
t->annotation().type->canBeUsedExternally(false),
|
||||||
|
"Internal type used as parameter for external function."
|
||||||
|
);
|
||||||
|
m_parameterTypes.push_back(t->annotation().type);
|
||||||
|
}
|
||||||
|
for (auto const& t: _typeName.returnParameterTypes())
|
||||||
|
{
|
||||||
|
solAssert(t->annotation().type, "Type not set for return parameter.");
|
||||||
|
if (m_location == Location::External)
|
||||||
|
solAssert(
|
||||||
|
t->annotation().type->canBeUsedExternally(false),
|
||||||
|
"Internal type used as return parameter for external function."
|
||||||
|
);
|
||||||
|
m_returnParameterTypes.push_back(t->annotation().type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _contract)
|
FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
FunctionDefinition const* constructor = _contract.constructor();
|
FunctionDefinition const* constructor = _contract.constructor();
|
||||||
@ -1875,22 +1928,69 @@ bool FunctionType::operator==(Type const& _other) const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
|
||||||
|
{
|
||||||
|
if (_operator == Token::Value::Delete)
|
||||||
|
return make_shared<TupleType>();
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
string FunctionType::canonicalName(bool) const
|
||||||
|
{
|
||||||
|
solAssert(m_location == Location::External, "");
|
||||||
|
return "function";
|
||||||
|
}
|
||||||
|
|
||||||
string FunctionType::toString(bool _short) const
|
string FunctionType::toString(bool _short) const
|
||||||
{
|
{
|
||||||
string name = "function (";
|
string name = "function (";
|
||||||
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
|
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
|
||||||
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
|
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
|
||||||
name += ") returns (";
|
name += ")";
|
||||||
for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
|
if (m_isConstant)
|
||||||
name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
|
name += " constant";
|
||||||
return name + ")";
|
if (m_isPayable)
|
||||||
|
name += " payable";
|
||||||
|
if (m_location == Location::External)
|
||||||
|
name += " external";
|
||||||
|
if (!m_returnParameterTypes.empty())
|
||||||
|
{
|
||||||
|
name += " returns (";
|
||||||
|
for (auto it = m_returnParameterTypes.begin(); it != m_returnParameterTypes.end(); ++it)
|
||||||
|
name += (*it)->toString(_short) + (it + 1 == m_returnParameterTypes.end() ? "" : ",");
|
||||||
|
name += ")";
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned FunctionType::calldataEncodedSize(bool _padded) const
|
||||||
|
{
|
||||||
|
unsigned size = storageBytes();
|
||||||
|
if (_padded)
|
||||||
|
size = ((size + 31) / 32) * 32;
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
u256 FunctionType::storageSize() const
|
u256 FunctionType::storageSize() const
|
||||||
{
|
{
|
||||||
BOOST_THROW_EXCEPTION(
|
if (m_location == Location::External || m_location == Location::Internal)
|
||||||
InternalCompilerError()
|
return 1;
|
||||||
<< errinfo_comment("Storage size of non-storable function type requested."));
|
else
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable function type requested."));
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned FunctionType::storageBytes() const
|
||||||
|
{
|
||||||
|
if (m_location == Location::External)
|
||||||
|
return 20 + 4;
|
||||||
|
else if (m_location == Location::Internal)
|
||||||
|
return 8; // it should really not be possible to create larger programs
|
||||||
|
else
|
||||||
|
BOOST_THROW_EXCEPTION(
|
||||||
|
InternalCompilerError()
|
||||||
|
<< errinfo_comment("Storage size of non-storable function type requested."));
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned FunctionType::sizeOnStack() const
|
unsigned FunctionType::sizeOnStack() const
|
||||||
@ -2013,6 +2113,23 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypePointer FunctionType::encodingType() const
|
||||||
|
{
|
||||||
|
// Only external functions can be encoded, internal functions cannot leave code boundaries.
|
||||||
|
if (m_location == Location::External)
|
||||||
|
return shared_from_this();
|
||||||
|
else
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
TypePointer FunctionType::interfaceType(bool /*_inLibrary*/) const
|
||||||
|
{
|
||||||
|
if (m_location == Location::External)
|
||||||
|
return shared_from_this();
|
||||||
|
else
|
||||||
|
return TypePointer();
|
||||||
|
}
|
||||||
|
|
||||||
bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
|
bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const
|
||||||
{
|
{
|
||||||
solAssert(!bound() || _selfType, "");
|
solAssert(!bound() || _selfType, "");
|
||||||
|
@ -254,6 +254,9 @@ public:
|
|||||||
/// @param _inLibrary if set, returns types as used in a library, e.g. struct and contract types
|
/// @param _inLibrary if set, returns types as used in a library, e.g. struct and contract types
|
||||||
/// are returned without modification.
|
/// are returned without modification.
|
||||||
virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); }
|
virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); }
|
||||||
|
/// @returns true iff this type can be passed on via calls (to libraries if _inLibrary is true),
|
||||||
|
/// should be have identical to !!interfaceType(_inLibrary) but might do optimizations.
|
||||||
|
virtual bool canBeUsedExternally(bool _inLibrary) const { return !!interfaceType(_inLibrary); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns a member list containing all members added to this type by `using for` directives.
|
/// @returns a member list containing all members added to this type by `using for` directives.
|
||||||
@ -580,6 +583,7 @@ public:
|
|||||||
virtual TypePointer encodingType() const override;
|
virtual TypePointer encodingType() const override;
|
||||||
virtual TypePointer decodingType() const override;
|
virtual TypePointer decodingType() const override;
|
||||||
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
||||||
|
virtual bool canBeUsedExternally(bool _inLibrary) const override;
|
||||||
|
|
||||||
/// @returns true if this is a byte array or a string
|
/// @returns true if this is a byte array or a string
|
||||||
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
|
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
|
||||||
@ -738,6 +742,7 @@ public:
|
|||||||
EnumDefinition const& enumDefinition() const { return m_enum; }
|
EnumDefinition const& enumDefinition() const { return m_enum; }
|
||||||
/// @returns the value that the string has in the Enum
|
/// @returns the value that the string has in the Enum
|
||||||
unsigned int memberValue(ASTString const& _member) const;
|
unsigned int memberValue(ASTString const& _member) const;
|
||||||
|
size_t numberOfMembers() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EnumDefinition const& m_enum;
|
EnumDefinition const& m_enum;
|
||||||
@ -820,6 +825,8 @@ public:
|
|||||||
explicit FunctionType(VariableDeclaration const& _varDecl);
|
explicit FunctionType(VariableDeclaration const& _varDecl);
|
||||||
/// Creates the function type of an event.
|
/// Creates the function type of an event.
|
||||||
explicit FunctionType(EventDefinition const& _event);
|
explicit FunctionType(EventDefinition const& _event);
|
||||||
|
/// Creates the type of a function type name.
|
||||||
|
explicit FunctionType(FunctionTypeName const& _typeName);
|
||||||
/// Function type constructor to be used for a plain type (not derived from a declaration).
|
/// Function type constructor to be used for a plain type (not derived from a declaration).
|
||||||
FunctionType(
|
FunctionType(
|
||||||
strings const& _parameterTypes,
|
strings const& _parameterTypes,
|
||||||
@ -889,12 +896,19 @@ public:
|
|||||||
TypePointer selfType() const;
|
TypePointer selfType() const;
|
||||||
|
|
||||||
virtual bool operator==(Type const& _other) const override;
|
virtual bool operator==(Type const& _other) const override;
|
||||||
|
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
|
||||||
|
virtual std::string canonicalName(bool /*_addDataLocation*/) const override;
|
||||||
virtual std::string toString(bool _short) const override;
|
virtual std::string toString(bool _short) const override;
|
||||||
virtual bool canBeStored() const override { return false; }
|
virtual unsigned calldataEncodedSize(bool _padded) const override;
|
||||||
|
virtual bool canBeStored() const override { return m_location == Location::Internal || m_location == Location::External; }
|
||||||
virtual u256 storageSize() const override;
|
virtual u256 storageSize() const override;
|
||||||
virtual bool canLiveOutsideStorage() const override { return false; }
|
virtual unsigned storageBytes() const override;
|
||||||
|
virtual bool isValueType() const override { return true; }
|
||||||
|
virtual bool canLiveOutsideStorage() const override { return m_location == Location::Internal || m_location == Location::External; }
|
||||||
virtual unsigned sizeOnStack() const override;
|
virtual unsigned sizeOnStack() const override;
|
||||||
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
|
||||||
|
virtual TypePointer encodingType() const override;
|
||||||
|
virtual TypePointer interfaceType(bool _inLibrary) const override;
|
||||||
|
|
||||||
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
|
/// @returns TypePointer of a new FunctionType object. All input/return parameters are an
|
||||||
/// appropriate external types (i.e. the interfaceType()s) of input/return parameters of
|
/// appropriate external types (i.e. the interfaceType()s) of input/return parameters of
|
||||||
|
@ -270,7 +270,7 @@ void ArrayUtils::copyArrayToStorage(ArrayType const& _targetType, ArrayType cons
|
|||||||
|
|
||||||
void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const
|
void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWordBoundaries) const
|
||||||
{
|
{
|
||||||
solAssert(
|
solUnimplementedAssert(
|
||||||
!_sourceType.baseType()->isDynamicallySized(),
|
!_sourceType.baseType()->isDynamicallySized(),
|
||||||
"Nested dynamic arrays not implemented here."
|
"Nested dynamic arrays not implemented here."
|
||||||
);
|
);
|
||||||
|
@ -33,14 +33,15 @@ void Compiler::compileContract(
|
|||||||
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ContractCompiler runtimeCompiler(m_runtimeContext, m_optimize);
|
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
|
||||||
runtimeCompiler.compileContract(_contract, _contracts);
|
runtimeCompiler.compileContract(_contract, _contracts);
|
||||||
|
|
||||||
ContractCompiler creationCompiler(m_context, m_optimize);
|
// This might modify m_runtimeContext because it can access runtime functions at
|
||||||
m_runtimeSub = creationCompiler.compileConstructor(m_runtimeContext, _contract, _contracts);
|
// creation time.
|
||||||
|
ContractCompiler creationCompiler(&runtimeCompiler, m_context, m_optimize);
|
||||||
|
m_runtimeSub = creationCompiler.compileConstructor(_contract, _contracts);
|
||||||
|
|
||||||
if (m_optimize)
|
m_context.optimise(m_optimize, m_optimizeRuns);
|
||||||
m_context.optimise(m_optimizeRuns);
|
|
||||||
|
|
||||||
if (_contract.isLibrary())
|
if (_contract.isLibrary())
|
||||||
{
|
{
|
||||||
@ -54,11 +55,11 @@ void Compiler::compileClone(
|
|||||||
map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ContractCompiler cloneCompiler(m_context, m_optimize);
|
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
|
||||||
|
ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize);
|
||||||
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
|
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
|
||||||
|
|
||||||
if (m_optimize)
|
m_context.optimise(m_optimize, m_optimizeRuns);
|
||||||
m_context.optimise(m_optimizeRuns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
|
eth::AssemblyItem Compiler::functionEntryLabel(FunctionDefinition const& _function) const
|
||||||
|
@ -35,7 +35,9 @@ class Compiler
|
|||||||
public:
|
public:
|
||||||
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
|
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
|
||||||
m_optimize(_optimize),
|
m_optimize(_optimize),
|
||||||
m_optimizeRuns(_runs)
|
m_optimizeRuns(_runs),
|
||||||
|
m_runtimeContext(),
|
||||||
|
m_context(&m_runtimeContext)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void compileContract(
|
void compileContract(
|
||||||
@ -69,9 +71,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool const m_optimize;
|
bool const m_optimize;
|
||||||
unsigned const m_optimizeRuns;
|
unsigned const m_optimizeRuns;
|
||||||
CompilerContext m_context;
|
|
||||||
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
|
|
||||||
CompilerContext m_runtimeContext;
|
CompilerContext m_runtimeContext;
|
||||||
|
size_t m_runtimeSub = size_t(-1); ///< Identifier of the runtime sub-assembly, if present.
|
||||||
|
CompilerContext m_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,8 @@ void CompilerContext::startFunction(Declaration const& _function)
|
|||||||
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
|
void CompilerContext::addVariable(VariableDeclaration const& _declaration,
|
||||||
unsigned _offsetToCurrent)
|
unsigned _offsetToCurrent)
|
||||||
{
|
{
|
||||||
solAssert(m_asm.deposit() >= 0 && unsigned(m_asm.deposit()) >= _offsetToCurrent, "");
|
solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
|
||||||
m_localVariables[&_declaration] = unsigned(m_asm.deposit()) - _offsetToCurrent;
|
m_localVariables[&_declaration] = unsigned(m_asm->deposit()) - _offsetToCurrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
|
void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
|
||||||
@ -92,22 +92,22 @@ eth::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration const&
|
|||||||
return m_functionCompilationQueue.entryLabelIfExists(_declaration);
|
return m_functionCompilationQueue.entryLabelIfExists(_declaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(FunctionDefinition const& _function)
|
FunctionDefinition const& CompilerContext::resolveVirtualFunction(FunctionDefinition const& _function)
|
||||||
{
|
{
|
||||||
// Libraries do not allow inheritance and their functions can be inlined, so we should not
|
// Libraries do not allow inheritance and their functions can be inlined, so we should not
|
||||||
// search the inheritance hierarchy (which will be the wrong one in case the function
|
// search the inheritance hierarchy (which will be the wrong one in case the function
|
||||||
// is inlined).
|
// is inlined).
|
||||||
if (auto scope = dynamic_cast<ContractDefinition const*>(_function.scope()))
|
if (auto scope = dynamic_cast<ContractDefinition const*>(_function.scope()))
|
||||||
if (scope->isLibrary())
|
if (scope->isLibrary())
|
||||||
return functionEntryLabel(_function);
|
return _function;
|
||||||
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
||||||
return virtualFunctionEntryLabel(_function, m_inheritanceHierarchy.begin());
|
return resolveVirtualFunction(_function, m_inheritanceHierarchy.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base)
|
FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base)
|
||||||
{
|
{
|
||||||
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set.");
|
||||||
return virtualFunctionEntryLabel(_function, superContract(_base));
|
return resolveVirtualFunction(_function, superContract(_base));
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const
|
FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const
|
||||||
@ -145,12 +145,12 @@ unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declarat
|
|||||||
|
|
||||||
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
|
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
|
||||||
{
|
{
|
||||||
return m_asm.deposit() - _baseOffset - 1;
|
return m_asm->deposit() - _baseOffset - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
|
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
|
||||||
{
|
{
|
||||||
return m_asm.deposit() - _offset - 1;
|
return m_asm->deposit() - _offset - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pair<u256, unsigned> CompilerContext::storageLocationOfVariable(const Declaration& _declaration) const
|
pair<u256, unsigned> CompilerContext::storageLocationOfVariable(const Declaration& _declaration) const
|
||||||
@ -217,17 +217,17 @@ void CompilerContext::appendInlineAssembly(
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, m_asm, identifierAccess), "");
|
solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
|
void CompilerContext::injectVersionStampIntoSub(size_t _subIndex)
|
||||||
{
|
{
|
||||||
eth::Assembly& sub = m_asm.sub(_subIndex);
|
eth::Assembly& sub = m_asm->sub(_subIndex);
|
||||||
sub.injectStart(Instruction::POP);
|
sub.injectStart(Instruction::POP);
|
||||||
sub.injectStart(fromBigEndian<u256>(binaryVersion()));
|
sub.injectStart(fromBigEndian<u256>(binaryVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
|
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
|
||||||
FunctionDefinition const& _function,
|
FunctionDefinition const& _function,
|
||||||
vector<ContractDefinition const*>::const_iterator _searchStart
|
vector<ContractDefinition const*>::const_iterator _searchStart
|
||||||
)
|
)
|
||||||
@ -242,9 +242,9 @@ eth::AssemblyItem CompilerContext::virtualFunctionEntryLabel(
|
|||||||
!function->isConstructor() &&
|
!function->isConstructor() &&
|
||||||
FunctionType(*function).hasEqualArgumentTypes(functionType)
|
FunctionType(*function).hasEqualArgumentTypes(functionType)
|
||||||
)
|
)
|
||||||
return functionEntryLabel(*function);
|
return *function;
|
||||||
solAssert(false, "Super function " + name + " not found.");
|
solAssert(false, "Super function " + name + " not found.");
|
||||||
return m_asm.newTag(); // not reached
|
return _function; // not reached
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<ContractDefinition const*>::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const
|
vector<ContractDefinition const*>::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const
|
||||||
@ -257,7 +257,7 @@ vector<ContractDefinition const*>::const_iterator CompilerContext::superContract
|
|||||||
|
|
||||||
void CompilerContext::updateSourceLocation()
|
void CompilerContext::updateSourceLocation()
|
||||||
{
|
{
|
||||||
m_asm.setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
|
m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
|
eth::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
|
||||||
|
@ -44,6 +44,14 @@ namespace solidity {
|
|||||||
class CompilerContext
|
class CompilerContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
CompilerContext(CompilerContext* _runtimeContext = nullptr):
|
||||||
|
m_asm(std::make_shared<eth::Assembly>()),
|
||||||
|
m_runtimeContext(_runtimeContext)
|
||||||
|
{
|
||||||
|
if (m_runtimeContext)
|
||||||
|
m_runtimeSub = size_t(m_asm->newSub(m_runtimeContext->m_asm).data());
|
||||||
|
}
|
||||||
|
|
||||||
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
void addMagicGlobal(MagicVariableDeclaration const& _declaration);
|
||||||
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
|
||||||
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
|
||||||
@ -52,9 +60,9 @@ public:
|
|||||||
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
|
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
|
||||||
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
|
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
|
||||||
|
|
||||||
void setStackOffset(int _offset) { m_asm.setDeposit(_offset); }
|
void setStackOffset(int _offset) { m_asm->setDeposit(_offset); }
|
||||||
void adjustStackOffset(int _adjustment) { m_asm.adjustDeposit(_adjustment); }
|
void adjustStackOffset(int _adjustment) { m_asm->adjustDeposit(_adjustment); }
|
||||||
unsigned stackHeight() const { solAssert(m_asm.deposit() >= 0, ""); return unsigned(m_asm.deposit()); }
|
unsigned stackHeight() const { solAssert(m_asm->deposit() >= 0, ""); return unsigned(m_asm->deposit()); }
|
||||||
|
|
||||||
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
|
bool isMagicGlobal(Declaration const* _declaration) const { return m_magicGlobals.count(_declaration) != 0; }
|
||||||
bool isLocalVariable(Declaration const* _declaration) const;
|
bool isLocalVariable(Declaration const* _declaration) const;
|
||||||
@ -67,10 +75,10 @@ public:
|
|||||||
eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const;
|
eth::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const;
|
||||||
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
|
void setInheritanceHierarchy(std::vector<ContractDefinition const*> const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; }
|
||||||
/// @returns the entry label of the given function and takes overrides into account.
|
/// @returns the entry label of the given function and takes overrides into account.
|
||||||
eth::AssemblyItem virtualFunctionEntryLabel(FunctionDefinition const& _function);
|
FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function);
|
||||||
/// @returns the entry label of a function that overrides the given declaration from the most derived class just
|
/// @returns the function that overrides the given declaration from the most derived class just
|
||||||
/// above _base in the current inheritance hierarchy.
|
/// above _base in the current inheritance hierarchy.
|
||||||
eth::AssemblyItem superFunctionEntryLabel(FunctionDefinition const& _function, ContractDefinition const& _base);
|
FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base);
|
||||||
FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const;
|
FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const;
|
||||||
|
|
||||||
/// @returns the next function in the queue of functions that are still to be compiled
|
/// @returns the next function in the queue of functions that are still to be compiled
|
||||||
@ -95,30 +103,33 @@ public:
|
|||||||
std::pair<u256, unsigned> storageLocationOfVariable(Declaration const& _declaration) const;
|
std::pair<u256, unsigned> storageLocationOfVariable(Declaration const& _declaration) const;
|
||||||
|
|
||||||
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
/// Appends a JUMPI instruction to a new tag and @returns the tag
|
||||||
eth::AssemblyItem appendConditionalJump() { return m_asm.appendJumpI().tag(); }
|
eth::AssemblyItem appendConditionalJump() { return m_asm->appendJumpI().tag(); }
|
||||||
/// Appends a JUMPI instruction to @a _tag
|
/// Appends a JUMPI instruction to @a _tag
|
||||||
CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJumpI(_tag); return *this; }
|
CompilerContext& appendConditionalJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJumpI(_tag); return *this; }
|
||||||
/// Appends a JUMP to a new tag and @returns the tag
|
/// Appends a JUMP to a new tag and @returns the tag
|
||||||
eth::AssemblyItem appendJumpToNew() { return m_asm.appendJump().tag(); }
|
eth::AssemblyItem appendJumpToNew() { return m_asm->appendJump().tag(); }
|
||||||
/// Appends a JUMP to a tag already on the stack
|
/// Appends a JUMP to a tag already on the stack
|
||||||
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
|
CompilerContext& appendJump(eth::AssemblyItem::JumpType _jumpType = eth::AssemblyItem::JumpType::Ordinary);
|
||||||
/// Returns an "ErrorTag"
|
/// Returns an "ErrorTag"
|
||||||
eth::AssemblyItem errorTag() { return m_asm.errorTag(); }
|
eth::AssemblyItem errorTag() { return m_asm->errorTag(); }
|
||||||
/// Appends a JUMP to a specific tag
|
/// Appends a JUMP to a specific tag
|
||||||
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm.appendJump(_tag); return *this; }
|
CompilerContext& appendJumpTo(eth::AssemblyItem const& _tag) { m_asm->appendJump(_tag); return *this; }
|
||||||
/// Appends pushing of a new tag and @returns the new tag.
|
/// Appends pushing of a new tag and @returns the new tag.
|
||||||
eth::AssemblyItem pushNewTag() { return m_asm.append(m_asm.newPushTag()).tag(); }
|
eth::AssemblyItem pushNewTag() { return m_asm->append(m_asm->newPushTag()).tag(); }
|
||||||
/// @returns a new tag without pushing any opcodes or data
|
/// @returns a new tag without pushing any opcodes or data
|
||||||
eth::AssemblyItem newTag() { return m_asm.newTag(); }
|
eth::AssemblyItem newTag() { return m_asm->newTag(); }
|
||||||
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
|
/// Adds a subroutine to the code (in the data section) and pushes its size (via a tag)
|
||||||
/// on the stack. @returns the assembly item corresponding to the pushed subroutine, i.e. its offset.
|
/// on the stack. @returns the pushsub assembly item.
|
||||||
eth::AssemblyItem addSubroutine(eth::Assembly const& _assembly) { return m_asm.appendSubSize(_assembly); }
|
eth::AssemblyItem addSubroutine(eth::AssemblyPointer const& _assembly) { auto sub = m_asm->newSub(_assembly); m_asm->append(m_asm->newPushSubSize(size_t(sub.data()))); return sub; }
|
||||||
|
void pushSubroutineSize(size_t _subRoutine) { m_asm->append(m_asm->newPushSubSize(_subRoutine)); }
|
||||||
|
/// Pushes the offset of the subroutine.
|
||||||
|
void pushSubroutineOffset(size_t _subRoutine) { m_asm->append(eth::AssemblyItem(eth::PushSub, _subRoutine)); }
|
||||||
/// Pushes the size of the final program
|
/// Pushes the size of the final program
|
||||||
void appendProgramSize() { return m_asm.appendProgramSize(); }
|
void appendProgramSize() { m_asm->appendProgramSize(); }
|
||||||
/// Adds data to the data section, pushes a reference to the stack
|
/// Adds data to the data section, pushes a reference to the stack
|
||||||
eth::AssemblyItem appendData(bytes const& _data) { return m_asm.append(_data); }
|
eth::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); }
|
||||||
/// Appends the address (virtual, will be filled in by linker) of a library.
|
/// Appends the address (virtual, will be filled in by linker) of a library.
|
||||||
void appendLibraryAddress(std::string const& _identifier) { m_asm.appendLibraryAddress(_identifier); }
|
void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); }
|
||||||
/// Resets the stack of visited nodes with a new stack having only @c _node
|
/// Resets the stack of visited nodes with a new stack having only @c _node
|
||||||
void resetVisitedNodes(ASTNode const* _node);
|
void resetVisitedNodes(ASTNode const* _node);
|
||||||
/// Pops the stack of visited nodes
|
/// Pops the stack of visited nodes
|
||||||
@ -127,10 +138,10 @@ public:
|
|||||||
void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
|
void pushVisitedNodes(ASTNode const* _node) { m_visitedNodes.push(_node); updateSourceLocation(); }
|
||||||
|
|
||||||
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
/// Append elements to the current instruction list and adjust @a m_stackOffset.
|
||||||
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm.append(_item); return *this; }
|
CompilerContext& operator<<(eth::AssemblyItem const& _item) { m_asm->append(_item); return *this; }
|
||||||
CompilerContext& operator<<(Instruction _instruction) { m_asm.append(_instruction); return *this; }
|
CompilerContext& operator<<(Instruction _instruction) { m_asm->append(_instruction); return *this; }
|
||||||
CompilerContext& operator<<(u256 const& _value) { m_asm.append(_value); return *this; }
|
CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
|
||||||
CompilerContext& operator<<(bytes const& _data) { m_asm.append(_data); return *this; }
|
CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }
|
||||||
|
|
||||||
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
|
/// Appends inline assembly. @a _replacements are string-matching replacements that are performed
|
||||||
/// prior to parsing the inline assembly.
|
/// prior to parsing the inline assembly.
|
||||||
@ -144,22 +155,27 @@ public:
|
|||||||
/// Prepends "PUSH <compiler version number> POP"
|
/// Prepends "PUSH <compiler version number> POP"
|
||||||
void injectVersionStampIntoSub(size_t _subIndex);
|
void injectVersionStampIntoSub(size_t _subIndex);
|
||||||
|
|
||||||
void optimise(unsigned _runs = 200) { m_asm.optimise(true, true, _runs); }
|
void optimise(bool _fullOptimsation, unsigned _runs = 200) { m_asm->optimise(_fullOptimsation, true, _runs); }
|
||||||
|
|
||||||
eth::Assembly const& assembly() const { return m_asm; }
|
/// @returns the runtime context if in creation mode and runtime context is set, nullptr otherwise.
|
||||||
|
CompilerContext* runtimeContext() { return m_runtimeContext; }
|
||||||
|
/// @returns the identifier of the runtime subroutine.
|
||||||
|
size_t runtimeSub() const { return m_runtimeSub; }
|
||||||
|
|
||||||
|
eth::Assembly const& assembly() const { return *m_asm; }
|
||||||
/// @returns non-const reference to the underlying assembly. Should be avoided in favour of
|
/// @returns non-const reference to the underlying assembly. Should be avoided in favour of
|
||||||
/// wrappers in this class.
|
/// wrappers in this class.
|
||||||
eth::Assembly& nonConstAssembly() { return m_asm; }
|
eth::Assembly& nonConstAssembly() { return *m_asm; }
|
||||||
|
|
||||||
/// @arg _sourceCodes is the map of input files to source code strings
|
/// @arg _sourceCodes is the map of input files to source code strings
|
||||||
/// @arg _inJsonFormat shows whether the out should be in Json format
|
/// @arg _inJsonFormat shows whether the out should be in Json format
|
||||||
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
|
Json::Value streamAssembly(std::ostream& _stream, StringMap const& _sourceCodes = StringMap(), bool _inJsonFormat = false) const
|
||||||
{
|
{
|
||||||
return m_asm.stream(_stream, "", _sourceCodes, _inJsonFormat);
|
return m_asm->stream(_stream, "", _sourceCodes, _inJsonFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::LinkerObject const& assembledObject() { return m_asm.assemble(); }
|
eth::LinkerObject const& assembledObject() { return m_asm->assemble(); }
|
||||||
eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm.sub(_subIndex).assemble(); }
|
eth::LinkerObject const& assembledRuntimeObject(size_t _subIndex) { return m_asm->sub(_subIndex).assemble(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to pop the visited nodes stack when a scope closes
|
* Helper class to pop the visited nodes stack when a scope closes
|
||||||
@ -172,9 +188,9 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns the entry label of the given function - searches the inheritance hierarchy
|
/// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns
|
||||||
/// startig from the given point towards the base.
|
/// the first function definition that is overwritten by _function.
|
||||||
eth::AssemblyItem virtualFunctionEntryLabel(
|
FunctionDefinition const& resolveVirtualFunction(
|
||||||
FunctionDefinition const& _function,
|
FunctionDefinition const& _function,
|
||||||
std::vector<ContractDefinition const*>::const_iterator _searchStart
|
std::vector<ContractDefinition const*>::const_iterator _searchStart
|
||||||
);
|
);
|
||||||
@ -215,7 +231,7 @@ private:
|
|||||||
mutable std::queue<Declaration const*> m_functionsToCompile;
|
mutable std::queue<Declaration const*> m_functionsToCompile;
|
||||||
} m_functionCompilationQueue;
|
} m_functionCompilationQueue;
|
||||||
|
|
||||||
eth::Assembly m_asm;
|
eth::AssemblyPointer m_asm;
|
||||||
/// Magic global variables like msg, tx or this, distinguished by type.
|
/// Magic global variables like msg, tx or this, distinguished by type.
|
||||||
std::set<Declaration const*> m_magicGlobals;
|
std::set<Declaration const*> m_magicGlobals;
|
||||||
/// Other already compiled contracts to be used in contract creation calls.
|
/// Other already compiled contracts to be used in contract creation calls.
|
||||||
@ -228,6 +244,10 @@ private:
|
|||||||
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
std::vector<ContractDefinition const*> m_inheritanceHierarchy;
|
||||||
/// Stack of current visited AST nodes, used for location attachment
|
/// Stack of current visited AST nodes, used for location attachment
|
||||||
std::stack<ASTNode const*> m_visitedNodes;
|
std::stack<ASTNode const*> m_visitedNodes;
|
||||||
|
/// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime.
|
||||||
|
CompilerContext *m_runtimeContext;
|
||||||
|
/// The index of the runtime subroutine.
|
||||||
|
size_t m_runtimeSub = -1;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -133,12 +133,22 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
|
|||||||
m_context << u256(str->value().size());
|
m_context << u256(str->value().size());
|
||||||
m_context << Instruction::ADD;
|
m_context << Instruction::ADD;
|
||||||
}
|
}
|
||||||
|
else if (
|
||||||
|
_type.category() == Type::Category::Function &&
|
||||||
|
dynamic_cast<FunctionType const&>(_type).location() == FunctionType::Location::External
|
||||||
|
)
|
||||||
|
{
|
||||||
|
solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
|
||||||
|
combineExternalFunctionType(true);
|
||||||
|
m_context << Instruction::DUP2 << Instruction::MSTORE;
|
||||||
|
m_context << u256(_padToWordBoundaries ? 32 : 24) << Instruction::ADD;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
|
unsigned numBytes = prepareMemoryStore(_type, _padToWordBoundaries);
|
||||||
if (numBytes > 0)
|
if (numBytes > 0)
|
||||||
{
|
{
|
||||||
solAssert(
|
solUnimplementedAssert(
|
||||||
_type.sizeOnStack() == 1,
|
_type.sizeOnStack() == 1,
|
||||||
"Memory store of types with stack size != 1 not implemented."
|
"Memory store of types with stack size != 1 not implemented."
|
||||||
);
|
);
|
||||||
@ -161,7 +171,7 @@ void CompilerUtils::encodeToMemory(
|
|||||||
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
solAssert(targetTypes.size() == _givenTypes.size(), "");
|
||||||
for (TypePointer& t: targetTypes)
|
for (TypePointer& t: targetTypes)
|
||||||
{
|
{
|
||||||
solAssert(
|
solUnimplementedAssert(
|
||||||
t->mobileType() &&
|
t->mobileType() &&
|
||||||
t->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
|
t->mobileType()->interfaceType(_encodeAsLibraryTypes) &&
|
||||||
t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
|
t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType(),
|
||||||
@ -206,7 +216,8 @@ void CompilerUtils::encodeToMemory(
|
|||||||
else if (
|
else if (
|
||||||
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
_givenTypes[i]->dataStoredIn(DataLocation::Storage) ||
|
||||||
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
|
_givenTypes[i]->dataStoredIn(DataLocation::CallData) ||
|
||||||
_givenTypes[i]->category() == Type::Category::StringLiteral
|
_givenTypes[i]->category() == Type::Category::StringLiteral ||
|
||||||
|
_givenTypes[i]->category() == Type::Category::Function
|
||||||
)
|
)
|
||||||
type = _givenTypes[i]; // delay conversion
|
type = _givenTypes[i]; // delay conversion
|
||||||
else
|
else
|
||||||
@ -304,6 +315,49 @@ void CompilerUtils::memoryCopy()
|
|||||||
m_context << Instruction::POP; // ignore return value
|
m_context << Instruction::POP; // ignore return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::splitExternalFunctionType(bool _leftAligned)
|
||||||
|
{
|
||||||
|
// We have to split the left-aligned <address><function identifier> into two stack slots:
|
||||||
|
// address (right aligned), function identifier (right aligned)
|
||||||
|
if (_leftAligned)
|
||||||
|
{
|
||||||
|
m_context << Instruction::DUP1 << (u256(1) << (64 + 32)) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
|
// <input> <address>
|
||||||
|
m_context << Instruction::SWAP1 << (u256(1) << 64) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_context << Instruction::DUP1 << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
|
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
|
||||||
|
}
|
||||||
|
m_context << u256(0xffffffffUL) << Instruction::AND;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::combineExternalFunctionType(bool _leftAligned)
|
||||||
|
{
|
||||||
|
// <address> <function_id>
|
||||||
|
m_context << u256(0xffffffffUL) << Instruction::AND << Instruction::SWAP1;
|
||||||
|
if (!_leftAligned)
|
||||||
|
m_context << ((u256(1) << 160) - 1) << Instruction::AND;
|
||||||
|
m_context << (u256(1) << 32) << Instruction::MUL;
|
||||||
|
m_context << Instruction::OR;
|
||||||
|
if (_leftAligned)
|
||||||
|
m_context << (u256(1) << 64) << Instruction::MUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
|
||||||
|
{
|
||||||
|
m_context << m_context.functionEntryLabel(_function).pushTag();
|
||||||
|
// If there is a runtime context, we have to merge both labels into the same
|
||||||
|
// stack slot in case we store it in storage.
|
||||||
|
if (CompilerContext* rtc = m_context.runtimeContext())
|
||||||
|
m_context <<
|
||||||
|
(u256(1) << 32) <<
|
||||||
|
Instruction::MUL <<
|
||||||
|
rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
|
||||||
|
Instruction::OR;
|
||||||
|
}
|
||||||
|
|
||||||
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded)
|
||||||
{
|
{
|
||||||
// For a type extension, we need to remove all higher-order bits that we might have ignored in
|
// For a type extension, we need to remove all higher-order bits that we might have ignored in
|
||||||
@ -315,6 +369,8 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
Type::Category stackTypeCategory = _typeOnStack.category();
|
Type::Category stackTypeCategory = _typeOnStack.category();
|
||||||
Type::Category targetTypeCategory = _targetType.category();
|
Type::Category targetTypeCategory = _targetType.category();
|
||||||
|
|
||||||
|
bool enumOverflowCheckPending = (targetTypeCategory == Type::Category::Enum || stackTypeCategory == Type::Category::Enum);
|
||||||
|
|
||||||
switch (stackTypeCategory)
|
switch (stackTypeCategory)
|
||||||
{
|
{
|
||||||
case Type::Category::FixedBytes:
|
case Type::Category::FixedBytes:
|
||||||
@ -348,10 +404,18 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Category::Enum:
|
case Type::Category::Enum:
|
||||||
solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Enum, "");
|
solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, "");
|
||||||
|
if (enumOverflowCheckPending)
|
||||||
|
{
|
||||||
|
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
|
||||||
|
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
|
||||||
|
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
|
||||||
|
m_context.appendConditionalJumpTo(m_context.errorTag());
|
||||||
|
enumOverflowCheckPending = false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Type::Category::FixedPoint:
|
case Type::Category::FixedPoint:
|
||||||
solAssert(false, "Not yet implemented - FixedPointType.");
|
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||||
case Type::Category::Integer:
|
case Type::Category::Integer:
|
||||||
case Type::Category::Contract:
|
case Type::Category::Contract:
|
||||||
case Type::Category::RationalNumber:
|
case Type::Category::RationalNumber:
|
||||||
@ -372,6 +436,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
solAssert(_typeOnStack.mobileType(), "");
|
solAssert(_typeOnStack.mobileType(), "");
|
||||||
// just clean
|
// just clean
|
||||||
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
|
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
|
||||||
|
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
|
||||||
|
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
|
||||||
|
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
|
||||||
|
m_context.appendConditionalJumpTo(m_context.errorTag());
|
||||||
|
enumOverflowCheckPending = false;
|
||||||
}
|
}
|
||||||
else if (targetTypeCategory == Type::Category::FixedPoint)
|
else if (targetTypeCategory == Type::Category::FixedPoint)
|
||||||
{
|
{
|
||||||
@ -386,7 +455,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
|
||||||
if (targetFixedPointType.integerBits() > typeOnStack->numBits())
|
if (targetFixedPointType.integerBits() > typeOnStack->numBits())
|
||||||
cleanHigherOrderBits(*typeOnStack);
|
cleanHigherOrderBits(*typeOnStack);
|
||||||
solAssert(false, "Not yet implemented - FixedPointType.");
|
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -399,7 +468,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_typeOnStack);
|
RationalNumberType const& constType = dynamic_cast<RationalNumberType const&>(_typeOnStack);
|
||||||
// We know that the stack is clean, we only have to clean for a narrowing conversion
|
// We know that the stack is clean, we only have to clean for a narrowing conversion
|
||||||
// where cleanup is forced.
|
// where cleanup is forced.
|
||||||
solAssert(!constType.isFractional(), "Not yet implemented - FixedPointType.");
|
solUnimplementedAssert(!constType.isFractional(), "Not yet implemented - FixedPointType.");
|
||||||
if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded)
|
if (targetType.numBits() < constType.integerType()->numBits() && _cleanupNeeded)
|
||||||
cleanHigherOrderBits(targetType);
|
cleanHigherOrderBits(targetType);
|
||||||
}
|
}
|
||||||
@ -651,15 +720,26 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
|
|||||||
solAssert(_targetType == _typeOnStack, "Invalid conversion for bool.");
|
solAssert(_targetType == _typeOnStack, "Invalid conversion for bool.");
|
||||||
if (_cleanupNeeded)
|
if (_cleanupNeeded)
|
||||||
m_context << Instruction::ISZERO << Instruction::ISZERO;
|
m_context << Instruction::ISZERO << Instruction::ISZERO;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// All other types should not be convertible to non-equal types.
|
// All other types should not be convertible to non-equal types.
|
||||||
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
|
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
solAssert(!enumOverflowCheckPending, "enum overflow checking missing.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompilerUtils::pushZeroValue(Type const& _type)
|
void CompilerUtils::pushZeroValue(Type const& _type)
|
||||||
{
|
{
|
||||||
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
|
{
|
||||||
|
if (funType->location() == FunctionType::Location::Internal)
|
||||||
|
{
|
||||||
|
m_context << m_context.errorTag();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
|
auto const* referenceType = dynamic_cast<ReferenceType const*>(&_type);
|
||||||
if (!referenceType || referenceType->location() == DataLocation::Storage)
|
if (!referenceType || referenceType->location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
@ -804,21 +884,27 @@ void CompilerUtils::storeStringData(bytesConstRef _data)
|
|||||||
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries)
|
||||||
{
|
{
|
||||||
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
|
unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries);
|
||||||
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
bool isExternalFunctionType = false;
|
||||||
|
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
|
||||||
|
if (funType->location() == FunctionType::Location::External)
|
||||||
|
isExternalFunctionType = true;
|
||||||
if (numBytes == 0)
|
if (numBytes == 0)
|
||||||
m_context << Instruction::POP << u256(0);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
|
m_context << Instruction::POP << u256(0);
|
||||||
m_context << (_fromCalldata ? Instruction::CALLDATALOAD : Instruction::MLOAD);
|
return numBytes;
|
||||||
if (numBytes != 32)
|
}
|
||||||
{
|
solAssert(numBytes <= 32, "Static memory load of more than 32 bytes requested.");
|
||||||
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
m_context << (_fromCalldata ? Instruction::CALLDATALOAD : Instruction::MLOAD);
|
||||||
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
|
if (isExternalFunctionType)
|
||||||
m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
|
splitExternalFunctionType(true);
|
||||||
if (leftAligned)
|
else if (numBytes != 32)
|
||||||
m_context << shiftFactor << Instruction::MUL;
|
{
|
||||||
}
|
bool leftAligned = _type.category() == Type::Category::FixedBytes;
|
||||||
|
// add leading or trailing zeros by dividing/multiplying depending on alignment
|
||||||
|
u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
|
||||||
|
m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
|
||||||
|
if (leftAligned)
|
||||||
|
m_context << shiftFactor << Instruction::MUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return numBytes;
|
return numBytes;
|
||||||
|
@ -114,6 +114,17 @@ public:
|
|||||||
/// Stack post:
|
/// Stack post:
|
||||||
void memoryCopy();
|
void memoryCopy();
|
||||||
|
|
||||||
|
/// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true)
|
||||||
|
/// external function type <address><function identifier> into two stack slots:
|
||||||
|
/// address (right aligned), function identifier (right aligned)
|
||||||
|
void splitExternalFunctionType(bool _rightAligned);
|
||||||
|
/// Performs the opposite operation of splitExternalFunctionType(_rightAligned)
|
||||||
|
void combineExternalFunctionType(bool _rightAligned);
|
||||||
|
/// Appends code that combines the construction-time (if available) and runtime function
|
||||||
|
/// entry label of the given function into a single stack slot.
|
||||||
|
/// Note: This might cause the compilation queue of the runtime context to be extended.
|
||||||
|
void pushCombinedFunctionEntryLabel(Declaration const& _function);
|
||||||
|
|
||||||
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
|
/// Appends code for an implicit or explicit type conversion. This includes erasing higher
|
||||||
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
|
/// order bits (@see appendHighBitCleanup) when widening integer but also copy to memory
|
||||||
/// if a reference type is converted from calldata or storage to memory.
|
/// if a reference type is converted from calldata or storage to memory.
|
||||||
|
@ -60,14 +60,13 @@ void ContractCompiler::compileContract(
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t ContractCompiler::compileConstructor(
|
size_t ContractCompiler::compileConstructor(
|
||||||
CompilerContext const& _runtimeContext,
|
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
CompilerContext::LocationSetter locationSetter(m_context, _contract);
|
||||||
initializeContext(_contract, _contracts);
|
initializeContext(_contract, _contracts);
|
||||||
return packIntoContractCreator(_contract, _runtimeContext);
|
return packIntoContractCreator(_contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ContractCompiler::compileClone(
|
size_t ContractCompiler::compileClone(
|
||||||
@ -80,7 +79,7 @@ size_t ContractCompiler::compileClone(
|
|||||||
appendInitAndConstructorCode(_contract);
|
appendInitAndConstructorCode(_contract);
|
||||||
|
|
||||||
//@todo determine largest return size of all runtime functions
|
//@todo determine largest return size of all runtime functions
|
||||||
eth::AssemblyItem runtimeSub = m_context.addSubroutine(cloneRuntime());
|
auto runtimeSub = m_context.addSubroutine(cloneRuntime());
|
||||||
|
|
||||||
// stack contains sub size
|
// stack contains sub size
|
||||||
m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
|
m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
|
||||||
@ -88,7 +87,6 @@ size_t ContractCompiler::compileClone(
|
|||||||
|
|
||||||
appendMissingFunctions();
|
appendMissingFunctions();
|
||||||
|
|
||||||
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
|
|
||||||
return size_t(runtimeSub.data());
|
return size_t(runtimeSub.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +102,13 @@ void ContractCompiler::initializeContext(
|
|||||||
m_context.resetVisitedNodes(&_contract);
|
m_context.resetVisitedNodes(&_contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ContractCompiler::appendCallValueCheck()
|
||||||
|
{
|
||||||
|
// Throw if function is not payable but call contained ether.
|
||||||
|
m_context << Instruction::CALLVALUE;
|
||||||
|
m_context.appendConditionalJumpTo(m_context.errorTag());
|
||||||
|
}
|
||||||
|
|
||||||
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
// Determine the arguments that are used for the base constructors.
|
// Determine the arguments that are used for the base constructors.
|
||||||
@ -139,23 +144,35 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
|
|||||||
appendConstructor(*constructor);
|
appendConstructor(*constructor);
|
||||||
else if (auto c = m_context.nextConstructor(_contract))
|
else if (auto c = m_context.nextConstructor(_contract))
|
||||||
appendBaseConstructor(*c);
|
appendBaseConstructor(*c);
|
||||||
|
else
|
||||||
|
appendCallValueCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext)
|
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
|
||||||
{
|
{
|
||||||
|
solAssert(!!m_runtimeCompiler, "");
|
||||||
|
|
||||||
appendInitAndConstructorCode(_contract);
|
appendInitAndConstructorCode(_contract);
|
||||||
|
|
||||||
eth::AssemblyItem runtimeSub = m_context.addSubroutine(_runtimeContext.assembly());
|
// We jump to the deploy routine because we first have to append all missing functions,
|
||||||
|
// which can cause further functions to be added to the runtime context.
|
||||||
|
eth::AssemblyItem deployRoutine = m_context.appendJumpToNew();
|
||||||
|
|
||||||
// stack contains sub size
|
// We have to include copies of functions in the construction time and runtime context
|
||||||
m_context << Instruction::DUP1 << runtimeSub << u256(0) << Instruction::CODECOPY;
|
// because of absolute jumps.
|
||||||
|
appendMissingFunctions();
|
||||||
|
m_runtimeCompiler->appendMissingFunctions();
|
||||||
|
|
||||||
|
m_context << deployRoutine;
|
||||||
|
|
||||||
|
solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
|
||||||
|
m_context.pushSubroutineSize(m_context.runtimeSub());
|
||||||
|
m_context << Instruction::DUP1;
|
||||||
|
m_context.pushSubroutineOffset(m_context.runtimeSub());
|
||||||
|
m_context << u256(0) << Instruction::CODECOPY;
|
||||||
m_context << u256(0) << Instruction::RETURN;
|
m_context << u256(0) << Instruction::RETURN;
|
||||||
|
|
||||||
// note that we have to include the functions again because of absolute jump labels
|
return m_context.runtimeSub();
|
||||||
appendMissingFunctions();
|
|
||||||
|
|
||||||
solAssert(runtimeSub.data() < numeric_limits<size_t>::max(), "");
|
|
||||||
return size_t(runtimeSub.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor)
|
||||||
@ -176,6 +193,9 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc
|
|||||||
void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor)
|
void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _constructor);
|
CompilerContext::LocationSetter locationSetter(m_context, _constructor);
|
||||||
|
if (!_constructor.isPayable())
|
||||||
|
appendCallValueCheck();
|
||||||
|
|
||||||
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
// copy constructor arguments from code to memory and then to stack, they are supplied after the actual program
|
||||||
if (!_constructor.parameters().empty())
|
if (!_constructor.parameters().empty())
|
||||||
{
|
{
|
||||||
@ -243,11 +263,8 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
|||||||
if (fallback)
|
if (fallback)
|
||||||
{
|
{
|
||||||
if (!fallback->isPayable())
|
if (!fallback->isPayable())
|
||||||
{
|
appendCallValueCheck();
|
||||||
// Throw if function is not payable but call contained ether.
|
|
||||||
m_context << Instruction::CALLVALUE;
|
|
||||||
m_context.appendConditionalJumpTo(m_context.errorTag());
|
|
||||||
}
|
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||||
fallback->accept(*this);
|
fallback->accept(*this);
|
||||||
m_context << returnTag;
|
m_context << returnTag;
|
||||||
@ -266,11 +283,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
|
|||||||
// We have to allow this for libraries, because value of the previous
|
// We have to allow this for libraries, because value of the previous
|
||||||
// call is still visible in the delegatecall.
|
// call is still visible in the delegatecall.
|
||||||
if (!functionType->isPayable() && !_contract.isLibrary())
|
if (!functionType->isPayable() && !_contract.isLibrary())
|
||||||
{
|
appendCallValueCheck();
|
||||||
// Throw if function is not payable but call contained ether.
|
|
||||||
m_context << Instruction::CALLVALUE;
|
|
||||||
m_context.appendConditionalJumpTo(m_context.errorTag());
|
|
||||||
}
|
|
||||||
|
|
||||||
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
eth::AssemblyItem returnTag = m_context.pushNewTag();
|
||||||
m_context << CompilerUtils::dataStartOffset;
|
m_context << CompilerUtils::dataStartOffset;
|
||||||
@ -293,13 +306,14 @@ void ContractCompiler::appendCalldataUnpacker(TypePointers const& _typeParameter
|
|||||||
{
|
{
|
||||||
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
// stack: v1 v2 ... v(k-1) base_offset current_offset
|
||||||
TypePointer type = parameterType->decodingType();
|
TypePointer type = parameterType->decodingType();
|
||||||
|
solAssert(type, "No decoding type found.");
|
||||||
if (type->category() == Type::Category::Array)
|
if (type->category() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
|
auto const& arrayType = dynamic_cast<ArrayType const&>(*type);
|
||||||
solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
|
solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
|
||||||
if (_fromMemory)
|
if (_fromMemory)
|
||||||
{
|
{
|
||||||
solAssert(
|
solUnimplementedAssert(
|
||||||
arrayType.baseType()->isValueType(),
|
arrayType.baseType()->isValueType(),
|
||||||
"Nested memory arrays not yet implemented here."
|
"Nested memory arrays not yet implemented here."
|
||||||
);
|
);
|
||||||
@ -515,7 +529,19 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
|
|||||||
{
|
{
|
||||||
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
|
solAssert(!!decl->type(), "Type of declaration required but not yet determined.");
|
||||||
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
|
if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(decl))
|
||||||
_assembly.append(m_context.virtualFunctionEntryLabel(*functionDef).pushTag());
|
{
|
||||||
|
functionDef = &m_context.resolveVirtualFunction(*functionDef);
|
||||||
|
_assembly.append(m_context.functionEntryLabel(*functionDef).pushTag());
|
||||||
|
// If there is a runtime context, we have to merge both labels into the same
|
||||||
|
// stack slot in case we store it in storage.
|
||||||
|
if (CompilerContext* rtc = m_context.runtimeContext())
|
||||||
|
{
|
||||||
|
_assembly.append(u256(1) << 32);
|
||||||
|
_assembly.append(Instruction::MUL);
|
||||||
|
_assembly.append(rtc->functionEntryLabel(*functionDef).toSubAssemblyTag(m_context.runtimeSub()));
|
||||||
|
_assembly.append(Instruction::OR);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
|
else if (auto variable = dynamic_cast<VariableDeclaration const*>(decl))
|
||||||
{
|
{
|
||||||
solAssert(!variable->isConstant(), "");
|
solAssert(!variable->isConstant(), "");
|
||||||
@ -611,12 +637,25 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
|
|||||||
m_breakTags.push_back(loopEnd);
|
m_breakTags.push_back(loopEnd);
|
||||||
|
|
||||||
m_context << loopStart;
|
m_context << loopStart;
|
||||||
compileExpression(_whileStatement.condition());
|
|
||||||
m_context << Instruction::ISZERO;
|
// While loops have the condition prepended
|
||||||
m_context.appendConditionalJumpTo(loopEnd);
|
if (!_whileStatement.isDoWhile())
|
||||||
|
{
|
||||||
|
compileExpression(_whileStatement.condition());
|
||||||
|
m_context << Instruction::ISZERO;
|
||||||
|
m_context.appendConditionalJumpTo(loopEnd);
|
||||||
|
}
|
||||||
|
|
||||||
_whileStatement.body().accept(*this);
|
_whileStatement.body().accept(*this);
|
||||||
|
|
||||||
|
// Do-while loops have the condition appended
|
||||||
|
if (_whileStatement.isDoWhile())
|
||||||
|
{
|
||||||
|
compileExpression(_whileStatement.condition());
|
||||||
|
m_context << Instruction::ISZERO;
|
||||||
|
m_context.appendConditionalJumpTo(loopEnd);
|
||||||
|
}
|
||||||
|
|
||||||
m_context.appendJumpTo(loopStart);
|
m_context.appendJumpTo(loopStart);
|
||||||
m_context << loopEnd;
|
m_context << loopEnd;
|
||||||
|
|
||||||
@ -858,7 +897,7 @@ void ContractCompiler::compileExpression(Expression const& _expression, TypePoin
|
|||||||
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
||||||
}
|
}
|
||||||
|
|
||||||
eth::Assembly ContractCompiler::cloneRuntime()
|
eth::AssemblyPointer ContractCompiler::cloneRuntime()
|
||||||
{
|
{
|
||||||
eth::Assembly a;
|
eth::Assembly a;
|
||||||
a << Instruction::CALLDATASIZE;
|
a << Instruction::CALLDATASIZE;
|
||||||
@ -876,5 +915,5 @@ eth::Assembly ContractCompiler::cloneRuntime()
|
|||||||
a.appendJumpI(a.errorTag());
|
a.appendJumpI(a.errorTag());
|
||||||
//@todo adjust for larger return values, make this dynamic.
|
//@todo adjust for larger return values, make this dynamic.
|
||||||
a << u256(0x20) << u256(0) << Instruction::RETURN;
|
a << u256(0x20) << u256(0) << Instruction::RETURN;
|
||||||
return a;
|
return make_shared<eth::Assembly>(a);
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,12 @@ namespace solidity {
|
|||||||
class ContractCompiler: private ASTConstVisitor
|
class ContractCompiler: private ASTConstVisitor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ContractCompiler(CompilerContext& _context, bool _optimise):
|
explicit ContractCompiler(ContractCompiler* _runtimeCompiler, CompilerContext& _context, bool _optimise):
|
||||||
m_optimise(_optimise),
|
m_optimise(_optimise),
|
||||||
|
m_runtimeCompiler(_runtimeCompiler),
|
||||||
m_context(_context)
|
m_context(_context)
|
||||||
{
|
{
|
||||||
m_context = CompilerContext();
|
m_context = CompilerContext(_runtimeCompiler ? &_runtimeCompiler->m_context : nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void compileContract(
|
void compileContract(
|
||||||
@ -52,7 +53,6 @@ public:
|
|||||||
/// Compiles the constructor part of the contract.
|
/// Compiles the constructor part of the contract.
|
||||||
/// @returns the identifier of the runtime sub-assembly.
|
/// @returns the identifier of the runtime sub-assembly.
|
||||||
size_t compileConstructor(
|
size_t compileConstructor(
|
||||||
CompilerContext const& _runtimeContext,
|
|
||||||
ContractDefinition const& _contract,
|
ContractDefinition const& _contract,
|
||||||
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts
|
||||||
);
|
);
|
||||||
@ -74,12 +74,13 @@ private:
|
|||||||
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
/// Adds the code that is run at creation time. Should be run after exchanging the run-time context
|
||||||
/// with a new and initialized context. Adds the constructor code.
|
/// with a new and initialized context. Adds the constructor code.
|
||||||
/// @returns the identifier of the runtime sub assembly
|
/// @returns the identifier of the runtime sub assembly
|
||||||
size_t packIntoContractCreator(ContractDefinition const& _contract, CompilerContext const& _runtimeContext);
|
size_t packIntoContractCreator(ContractDefinition const& _contract);
|
||||||
/// Appends state variable initialisation and constructor code.
|
/// Appends state variable initialisation and constructor code.
|
||||||
void appendInitAndConstructorCode(ContractDefinition const& _contract);
|
void appendInitAndConstructorCode(ContractDefinition const& _contract);
|
||||||
void appendBaseConstructor(FunctionDefinition const& _constructor);
|
void appendBaseConstructor(FunctionDefinition const& _constructor);
|
||||||
void appendConstructor(FunctionDefinition const& _constructor);
|
void appendConstructor(FunctionDefinition const& _constructor);
|
||||||
void appendFunctionSelector(ContractDefinition const& _contract);
|
void appendFunctionSelector(ContractDefinition const& _contract);
|
||||||
|
void appendCallValueCheck();
|
||||||
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
|
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
|
||||||
/// From memory if @a _fromMemory is true, otherwise from call data.
|
/// From memory if @a _fromMemory is true, otherwise from call data.
|
||||||
/// Expects source offset on the stack, which is removed.
|
/// Expects source offset on the stack, which is removed.
|
||||||
@ -114,9 +115,11 @@ private:
|
|||||||
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
void compileExpression(Expression const& _expression, TypePointer const& _targetType = TypePointer());
|
||||||
|
|
||||||
/// @returns the runtime assembly for clone contracts.
|
/// @returns the runtime assembly for clone contracts.
|
||||||
static eth::Assembly cloneRuntime();
|
static eth::AssemblyPointer cloneRuntime();
|
||||||
|
|
||||||
bool const m_optimise;
|
bool const m_optimise;
|
||||||
|
/// Pointer to the runtime compiler in case this is a creation compiler.
|
||||||
|
ContractCompiler* m_runtimeCompiler = nullptr;
|
||||||
CompilerContext& m_context;
|
CompilerContext& m_context;
|
||||||
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
|
std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
|
||||||
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
|
||||||
|
@ -99,7 +99,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
|||||||
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
if (auto mappingType = dynamic_cast<MappingType const*>(returnType.get()))
|
||||||
{
|
{
|
||||||
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
|
||||||
solAssert(
|
solUnimplementedAssert(
|
||||||
!paramTypes[i]->isDynamicallySized(),
|
!paramTypes[i]->isDynamicallySized(),
|
||||||
"Accessors for mapping with dynamically-sized keys not yet implemented."
|
"Accessors for mapping with dynamically-sized keys not yet implemented."
|
||||||
);
|
);
|
||||||
@ -211,7 +211,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
|||||||
Token::Value op = _assignment.assignmentOperator();
|
Token::Value op = _assignment.assignmentOperator();
|
||||||
if (op != Token::Assign) // compound assignment
|
if (op != Token::Assign) // compound assignment
|
||||||
{
|
{
|
||||||
solAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
|
solUnimplementedAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
|
||||||
unsigned lvalueSize = m_currentLValue->sizeOnStack();
|
unsigned lvalueSize = m_currentLValue->sizeOnStack();
|
||||||
unsigned itemSize = _assignment.annotation().type->sizeOnStack();
|
unsigned itemSize = _assignment.annotation().type->sizeOnStack();
|
||||||
if (lvalueSize > 0)
|
if (lvalueSize > 0)
|
||||||
@ -312,7 +312,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
|||||||
if (!_unaryOperation.isPrefixOperation())
|
if (!_unaryOperation.isPrefixOperation())
|
||||||
{
|
{
|
||||||
// store value for later
|
// store value for later
|
||||||
solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
solUnimplementedAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||||
m_context << Instruction::DUP1;
|
m_context << Instruction::DUP1;
|
||||||
if (m_currentLValue->sizeOnStack() > 0)
|
if (m_currentLValue->sizeOnStack() > 0)
|
||||||
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||||
@ -488,6 +488,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
parameterSize += function.selfType()->sizeOnStack();
|
parameterSize += function.selfType()->sizeOnStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_context.runtimeContext())
|
||||||
|
// We have a runtime context, so we need the creation part.
|
||||||
|
m_context << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
|
||||||
|
else
|
||||||
|
// Extract the runtime part.
|
||||||
|
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||||
|
|
||||||
m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
|
m_context.appendJump(eth::AssemblyItem::JumpType::IntoFunction);
|
||||||
m_context << returnLabel;
|
m_context << returnLabel;
|
||||||
|
|
||||||
@ -521,8 +528,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
// copy the contract's code into memory
|
// copy the contract's code into memory
|
||||||
eth::Assembly const& assembly = m_context.compiledContract(contract);
|
eth::Assembly const& assembly = m_context.compiledContract(contract);
|
||||||
utils().fetchFreeMemoryPointer();
|
utils().fetchFreeMemoryPointer();
|
||||||
|
// TODO we create a copy here, which is actually what we want.
|
||||||
|
// This should be revisited at the point where we fix
|
||||||
|
// https://github.com/ethereum/solidity/issues/1092
|
||||||
// pushes size
|
// pushes size
|
||||||
eth::AssemblyItem subroutine = m_context.addSubroutine(assembly);
|
auto subroutine = m_context.addSubroutine(make_shared<eth::Assembly>(assembly));
|
||||||
m_context << Instruction::DUP1 << subroutine;
|
m_context << Instruction::DUP1 << subroutine;
|
||||||
m_context << Instruction::DUP4 << Instruction::CODECOPY;
|
m_context << Instruction::DUP4 << Instruction::CODECOPY;
|
||||||
|
|
||||||
@ -845,9 +855,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
);
|
);
|
||||||
if (funType->location() == FunctionType::Location::Internal)
|
if (funType->location() == FunctionType::Location::Internal)
|
||||||
{
|
{
|
||||||
m_context << m_context.functionEntryLabel(
|
FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration());
|
||||||
dynamic_cast<FunctionDefinition const&>(funType->declaration())
|
utils().pushCombinedFunctionEntryLabel(funDef);
|
||||||
).pushTag();
|
|
||||||
utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1);
|
utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -883,7 +892,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
// us to link against it although we actually do not need it.
|
// us to link against it although we actually do not need it.
|
||||||
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
|
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
|
||||||
solAssert(!!function, "Function not found in member access");
|
solAssert(!!function, "Function not found in member access");
|
||||||
m_context << m_context.functionEntryLabel(*function).pushTag();
|
utils().pushCombinedFunctionEntryLabel(*function);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get()))
|
else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get()))
|
||||||
@ -915,10 +924,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
if (type.isSuper())
|
if (type.isSuper())
|
||||||
{
|
{
|
||||||
solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
|
solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
|
||||||
m_context << m_context.superFunctionEntryLabel(
|
utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
|
||||||
dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
|
dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
|
||||||
type.contractDefinition()
|
type.contractDefinition()
|
||||||
).pushTag();
|
));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1141,7 +1150,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
|||||||
break;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
//@todo if we implement this, the value in calldata has to be added to the base offset
|
//@todo if we implement this, the value in calldata has to be added to the base offset
|
||||||
solAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
|
solUnimplementedAssert(!arrayType.baseType()->isDynamicallySized(), "Nested arrays not yet implemented.");
|
||||||
if (arrayType.baseType()->isValueType())
|
if (arrayType.baseType()->isValueType())
|
||||||
CompilerUtils(m_context).loadFromMemoryDynamic(
|
CompilerUtils(m_context).loadFromMemoryDynamic(
|
||||||
*arrayType.baseType(),
|
*arrayType.baseType(),
|
||||||
@ -1203,7 +1212,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||||
m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
|
utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef));
|
||||||
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||||
appendVariable(*variable, static_cast<Expression const&>(_identifier));
|
appendVariable(*variable, static_cast<Expression const&>(_identifier));
|
||||||
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||||
@ -1266,6 +1275,17 @@ void ExpressionCompiler::appendCompareOperatorCode(Token::Value _operator, Type
|
|||||||
{
|
{
|
||||||
if (_operator == Token::Equal || _operator == Token::NotEqual)
|
if (_operator == Token::Equal || _operator == Token::NotEqual)
|
||||||
{
|
{
|
||||||
|
if (FunctionType const* funType = dynamic_cast<decltype(funType)>(&_type))
|
||||||
|
{
|
||||||
|
if (funType->location() == FunctionType::Location::Internal)
|
||||||
|
{
|
||||||
|
// We have to remove the upper bits (construction time value) because they might
|
||||||
|
// be "unknown" in one of the operands and not in the other.
|
||||||
|
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||||
|
m_context << Instruction::SWAP1;
|
||||||
|
m_context << ((u256(1) << 32) - 1) << Instruction::AND;
|
||||||
|
}
|
||||||
|
}
|
||||||
m_context << Instruction::EQ;
|
m_context << Instruction::EQ;
|
||||||
if (_operator == Token::NotEqual)
|
if (_operator == Token::NotEqual)
|
||||||
m_context << Instruction::ISZERO;
|
m_context << Instruction::ISZERO;
|
||||||
@ -1318,7 +1338,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Ty
|
|||||||
bool const c_isSigned = type.isSigned();
|
bool const c_isSigned = type.isSigned();
|
||||||
|
|
||||||
if (_type.category() == Type::Category::FixedPoint)
|
if (_type.category() == Type::Category::FixedPoint)
|
||||||
solAssert(false, "Not yet implemented - FixedPointType.");
|
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||||
|
|
||||||
switch (_operator)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
@ -1372,7 +1392,7 @@ void ExpressionCompiler::appendBitOperatorCode(Token::Value _operator)
|
|||||||
|
|
||||||
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
|
void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator)
|
||||||
{
|
{
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Shift operators not yet implemented."));
|
BOOST_THROW_EXCEPTION(UnimplementedFeatureError() << errinfo_comment("Shift operators not yet implemented."));
|
||||||
switch (_operator)
|
switch (_operator)
|
||||||
{
|
{
|
||||||
case Token::SHL:
|
case Token::SHL:
|
||||||
@ -1634,7 +1654,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
|||||||
|
|
||||||
void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
|
void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression)
|
||||||
{
|
{
|
||||||
solAssert(_expectedType.isValueType(), "Not implemented for non-value types.");
|
solUnimplementedAssert(_expectedType.isValueType(), "Not implemented for non-value types.");
|
||||||
_expression.accept(*this);
|
_expression.accept(*this);
|
||||||
utils().convertType(*_expression.annotation().type, _expectedType, true);
|
utils().convertType(*_expression.annotation().type, _expectedType, true);
|
||||||
utils().storeInMemoryDynamic(_expectedType);
|
utils().storeInMemoryDynamic(_expectedType);
|
||||||
|
@ -120,7 +120,7 @@ void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(_sourceType == *m_dataType, "Conversion not implemented for assignment to memory.");
|
solUnimplementedAssert(_sourceType == *m_dataType, "Conversion not implemented for assignment to memory.");
|
||||||
|
|
||||||
solAssert(m_dataType->sizeOnStack() == 1, "");
|
solAssert(m_dataType->sizeOnStack() == 1, "");
|
||||||
if (!_move)
|
if (!_move)
|
||||||
@ -153,7 +153,8 @@ StorageItem::StorageItem(CompilerContext& _compilerContext, Type const& _type):
|
|||||||
{
|
{
|
||||||
if (m_dataType->isValueType())
|
if (m_dataType->isValueType())
|
||||||
{
|
{
|
||||||
solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
|
if (m_dataType->category() != Type::Category::Function)
|
||||||
|
solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), "");
|
||||||
solAssert(m_dataType->storageSize() == 1, "Invalid storage size.");
|
solAssert(m_dataType->storageSize() == 1, "Invalid storage size.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,21 +177,39 @@ void StorageItem::retrieveValue(SourceLocation const&, bool _remove) const
|
|||||||
m_context << Instruction::POP << Instruction::SLOAD;
|
m_context << Instruction::POP << Instruction::SLOAD;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
bool cleaned = false;
|
||||||
m_context
|
m_context
|
||||||
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
|
<< Instruction::SWAP1 << Instruction::SLOAD << Instruction::SWAP1
|
||||||
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
|
<< u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV;
|
||||||
if (m_dataType->category() == Type::Category::FixedPoint)
|
if (m_dataType->category() == Type::Category::FixedPoint)
|
||||||
// implementation should be very similar to the integer case.
|
// implementation should be very similar to the integer case.
|
||||||
solAssert(false, "Not yet implemented - FixedPointType.");
|
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
|
{
|
||||||
m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL;
|
m_context << (u256(0x1) << (256 - 8 * m_dataType->storageBytes())) << Instruction::MUL;
|
||||||
|
cleaned = true;
|
||||||
|
}
|
||||||
else if (
|
else if (
|
||||||
m_dataType->category() == Type::Category::Integer &&
|
m_dataType->category() == Type::Category::Integer &&
|
||||||
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
dynamic_cast<IntegerType const&>(*m_dataType).isSigned()
|
||||||
)
|
)
|
||||||
|
{
|
||||||
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
m_context << u256(m_dataType->storageBytes() - 1) << Instruction::SIGNEXTEND;
|
||||||
else
|
cleaned = true;
|
||||||
|
}
|
||||||
|
else if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
|
||||||
|
{
|
||||||
|
if (fun->location() == FunctionType::Location::External)
|
||||||
|
{
|
||||||
|
CompilerUtils(m_context).splitExternalFunctionType(false);
|
||||||
|
cleaned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!cleaned)
|
||||||
|
{
|
||||||
|
solAssert(m_dataType->sizeOnStack() == 1, "");
|
||||||
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
|
m_context << ((u256(0x1) << (8 * m_dataType->storageBytes())) - 1) << Instruction::AND;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,6 +223,7 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size.");
|
solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size.");
|
||||||
if (m_dataType->storageBytes() == 32)
|
if (m_dataType->storageBytes() == 32)
|
||||||
{
|
{
|
||||||
|
solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size.");
|
||||||
// offset should be zero
|
// offset should be zero
|
||||||
m_context << Instruction::POP;
|
m_context << Instruction::POP;
|
||||||
if (!_move)
|
if (!_move)
|
||||||
@ -222,16 +242,27 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
m_context
|
m_context
|
||||||
<< Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
<< Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1)
|
||||||
<< Instruction::MUL;
|
<< Instruction::MUL;
|
||||||
m_context << Instruction::NOT << Instruction::AND;
|
m_context << Instruction::NOT << Instruction::AND << Instruction::SWAP1;
|
||||||
// stack: value storage_ref multiplier cleared_value
|
// stack: value storage_ref cleared_value multiplier
|
||||||
m_context
|
utils.copyToStackTop(3 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack());
|
||||||
<< Instruction::SWAP1 << Instruction::DUP4;
|
|
||||||
// stack: value storage_ref cleared_value multiplier value
|
// stack: value storage_ref cleared_value multiplier value
|
||||||
if (m_dataType->category() == Type::Category::FixedBytes)
|
if (FunctionType const* fun = dynamic_cast<decltype(fun)>(m_dataType))
|
||||||
|
{
|
||||||
|
if (fun->location() == FunctionType::Location::External)
|
||||||
|
// Combine the two-item function type into a single stack slot.
|
||||||
|
utils.combineExternalFunctionType(false);
|
||||||
|
else
|
||||||
|
m_context <<
|
||||||
|
((u256(1) << (8 * m_dataType->storageBytes())) - 1) <<
|
||||||
|
Instruction::AND;
|
||||||
|
}
|
||||||
|
else if (m_dataType->category() == Type::Category::FixedBytes)
|
||||||
m_context
|
m_context
|
||||||
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
|
<< (u256(0x1) << (256 - 8 * dynamic_cast<FixedBytesType const&>(*m_dataType).numBytes()))
|
||||||
<< Instruction::SWAP1 << Instruction::DIV;
|
<< Instruction::SWAP1 << Instruction::DIV;
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size for opaque type.");
|
||||||
// remove the higher order bits
|
// remove the higher order bits
|
||||||
m_context
|
m_context
|
||||||
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
|
<< (u256(1) << (8 * (32 - m_dataType->storageBytes())))
|
||||||
@ -239,11 +270,12 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
<< Instruction::DUP2
|
<< Instruction::DUP2
|
||||||
<< Instruction::MUL
|
<< Instruction::MUL
|
||||||
<< Instruction::DIV;
|
<< Instruction::DIV;
|
||||||
|
}
|
||||||
m_context << Instruction::MUL << Instruction::OR;
|
m_context << Instruction::MUL << Instruction::OR;
|
||||||
// stack: value storage_ref updated_value
|
// stack: value storage_ref updated_value
|
||||||
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
m_context << Instruction::SWAP1 << Instruction::SSTORE;
|
||||||
if (_move)
|
if (_move)
|
||||||
m_context << Instruction::POP;
|
utils.popStackElement(*m_dataType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -410,6 +410,16 @@ bool Why3Translator::visit(WhileStatement const& _node)
|
|||||||
{
|
{
|
||||||
addSourceFromDocStrings(_node.annotation());
|
addSourceFromDocStrings(_node.annotation());
|
||||||
|
|
||||||
|
// Why3 does not appear to support do-while loops,
|
||||||
|
// so we will simulate them by performing a while
|
||||||
|
// loop with the body prepended once.
|
||||||
|
|
||||||
|
if (_node.isDoWhile())
|
||||||
|
{
|
||||||
|
visitIndentedUnlessBlock(_node.body());
|
||||||
|
newLine();
|
||||||
|
}
|
||||||
|
|
||||||
add("while ");
|
add("while ");
|
||||||
_node.condition().accept(*this);
|
_node.condition().accept(*this);
|
||||||
newLine();
|
newLine();
|
||||||
|
@ -22,7 +22,7 @@ StructDefinition = 'struct' Identifier '{'
|
|||||||
( VariableDeclaration ';' (VariableDeclaration ';')* )? '}'
|
( VariableDeclaration ';' (VariableDeclaration ';')* )? '}'
|
||||||
ModifierDefinition = 'modifier' Identifier ParameterList? Block
|
ModifierDefinition = 'modifier' Identifier ParameterList? Block
|
||||||
FunctionDefinition = 'function' Identifier? ParameterList
|
FunctionDefinition = 'function' Identifier? ParameterList
|
||||||
( FunctionCall | Identifier | 'constant' | 'external' | 'public' | 'internal' | 'private' )*
|
( FunctionCall | Identifier | 'constant' |' payable' | 'external' | 'public' | 'internal' | 'private' )*
|
||||||
( 'returns' ParameterList )? Block
|
( 'returns' ParameterList )? Block
|
||||||
EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';'
|
EventDefinition = 'event' Identifier IndexedParameterList 'anonymous'? ';'
|
||||||
|
|
||||||
@ -31,18 +31,21 @@ EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}'
|
|||||||
|
|
||||||
IndexedParameterList = '(' ( TypeName 'indexed'? Identifier? (',' TypeName 'indexed'? Identifier?)* )? ')'
|
IndexedParameterList = '(' ( TypeName 'indexed'? Identifier? (',' TypeName 'indexed'? Identifier?)* )? ')'
|
||||||
ParameterList = '(' ( TypeName Identifier? (',' TypeName Identifier?)* )? ')'
|
ParameterList = '(' ( TypeName Identifier? (',' TypeName Identifier?)* )? ')'
|
||||||
|
TypeNameList = '(' ( TypeName (',' TypeName )* )? ')'
|
||||||
|
|
||||||
// semantic restriction: mappings and structs (recursively) containing mappings
|
// semantic restriction: mappings and structs (recursively) containing mappings
|
||||||
// are not allowed in argument lists
|
// are not allowed in argument lists
|
||||||
VariableDeclaration = TypeName Identifier
|
VariableDeclaration = TypeName Identifier
|
||||||
TypeName = ElementaryTypeName | Identifier StorageLocation? | Mapping | ArrayTypeName
|
TypeName = ElementaryTypeName | Identifier StorageLocation? | Mapping | ArrayTypeName | FunctionTypeName
|
||||||
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')'
|
||||||
ArrayTypeName = TypeName StorageLocation? '[' Expression? ']'
|
ArrayTypeName = TypeName StorageLocation? '[' Expression? ']'
|
||||||
|
FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | 'constant' | 'payable' )*
|
||||||
|
( 'returns' TypeNameList )?
|
||||||
StorageLocation = 'memory' | 'storage'
|
StorageLocation = 'memory' | 'storage'
|
||||||
|
|
||||||
Block = '{' Statement* '}'
|
Block = '{' Statement* '}'
|
||||||
Statement = IfStatement | WhileStatement | ForStatement | Block |
|
Statement = IfStatement | WhileStatement | ForStatement | Block |
|
||||||
( PlaceholderStatement | Continue | Break | Return |
|
( DoWhileStatement | PlaceholderStatement | Continue | Break | Return |
|
||||||
Throw | SimpleStatement ) ';'
|
Throw | SimpleStatement ) ';'
|
||||||
|
|
||||||
ExpressionStatement = Expression
|
ExpressionStatement = Expression
|
||||||
@ -51,6 +54,7 @@ WhileStatement = 'while' '(' Expression ')' Statement
|
|||||||
PlaceholderStatement = '_'
|
PlaceholderStatement = '_'
|
||||||
SimpleStatement = VariableDefinition | ExpressionStatement
|
SimpleStatement = VariableDefinition | ExpressionStatement
|
||||||
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
|
ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement
|
||||||
|
DoWhileStatement = 'do' Statement 'while' '(' Expression ')'
|
||||||
Continue = 'continue'
|
Continue = 'continue'
|
||||||
Break = 'break'
|
Break = 'break'
|
||||||
Return = 'return' Expression?
|
Return = 'return' Expression?
|
||||||
@ -64,7 +68,7 @@ Expression =
|
|||||||
| Expression '**' Expression
|
| Expression '**' Expression
|
||||||
| Expression ('*' | '/' | '%') Expression
|
| Expression ('*' | '/' | '%') Expression
|
||||||
| Expression ('+' | '-') Expression
|
| Expression ('+' | '-') Expression
|
||||||
| Expression ('<<' | '>>' | '>>>')
|
| Expression ('<<' | '>>')
|
||||||
| Expression '&' Expression
|
| Expression '&' Expression
|
||||||
| Expression '^' Expression
|
| Expression '^' Expression
|
||||||
| Expression '|' Expression
|
| Expression '|' Expression
|
||||||
|
@ -81,7 +81,11 @@ struct GeneratorState
|
|||||||
class LabelOrganizer: public boost::static_visitor<>
|
class LabelOrganizer: public boost::static_visitor<>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LabelOrganizer(GeneratorState& _state): m_state(_state) {}
|
LabelOrganizer(GeneratorState& _state): m_state(_state)
|
||||||
|
{
|
||||||
|
// Make the Solidity ErrorTag available to inline assembly
|
||||||
|
m_state.labels.insert(make_pair("invalidJumpLabel", m_state.assembly.errorTag()));
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void operator()(T const& /*_item*/) { }
|
void operator()(T const& /*_item*/) { }
|
||||||
@ -216,10 +220,18 @@ public:
|
|||||||
size_t numVariables = m_state.variables.size();
|
size_t numVariables = m_state.variables.size();
|
||||||
int deposit = m_state.assembly.deposit();
|
int deposit = m_state.assembly.deposit();
|
||||||
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
|
||||||
deposit = m_state.assembly.deposit() - deposit;
|
|
||||||
|
// pop variables
|
||||||
|
while (m_state.variables.size() > numVariables)
|
||||||
|
{
|
||||||
|
m_state.assembly.append(solidity::Instruction::POP);
|
||||||
|
m_state.variables.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
m_state.assembly.setSourceLocation(_block.location);
|
m_state.assembly.setSourceLocation(_block.location);
|
||||||
|
|
||||||
|
deposit = m_state.assembly.deposit() - deposit;
|
||||||
|
|
||||||
// issue warnings for stack height discrepancies
|
// issue warnings for stack height discrepancies
|
||||||
if (deposit < 0)
|
if (deposit < 0)
|
||||||
{
|
{
|
||||||
@ -238,12 +250,6 @@ public:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pop variables
|
|
||||||
while (m_state.variables.size() > numVariables)
|
|
||||||
{
|
|
||||||
m_state.assembly.append(solidity::Instruction::POP);
|
|
||||||
m_state.variables.pop_back();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include <libsolidity/interface/InterfaceHandler.h>
|
#include <libsolidity/interface/InterfaceHandler.h>
|
||||||
#include <libsolidity/formal/Why3Translator.h>
|
#include <libsolidity/formal/Why3Translator.h>
|
||||||
|
|
||||||
|
#include <libevmasm/Exceptions.h>
|
||||||
#include <libdevcore/SHA3.h>
|
#include <libdevcore/SHA3.h>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
@ -345,17 +346,17 @@ map<string, unsigned> CompilerStack::sourceIndices() const
|
|||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
string const& CompilerStack::interface(string const& _contractName) const
|
Json::Value const& CompilerStack::interface(string const& _contractName) const
|
||||||
{
|
{
|
||||||
return metadata(_contractName, DocumentationType::ABIInterface);
|
return metadata(_contractName, DocumentationType::ABIInterface);
|
||||||
}
|
}
|
||||||
|
|
||||||
string const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
|
Json::Value const& CompilerStack::metadata(string const& _contractName, DocumentationType _type) const
|
||||||
{
|
{
|
||||||
if (!m_parseSuccessful)
|
if (!m_parseSuccessful)
|
||||||
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Parsing was not successful."));
|
||||||
|
|
||||||
std::unique_ptr<string const>* doc;
|
std::unique_ptr<Json::Value const>* doc;
|
||||||
Contract const& currentContract = contract(_contractName);
|
Contract const& currentContract = contract(_contractName);
|
||||||
|
|
||||||
// checks wheather we already have the documentation
|
// checks wheather we already have the documentation
|
||||||
@ -376,7 +377,7 @@ string const& CompilerStack::metadata(string const& _contractName, Documentation
|
|||||||
|
|
||||||
// caches the result
|
// caches the result
|
||||||
if (!*doc)
|
if (!*doc)
|
||||||
doc->reset(new string(InterfaceHandler::documentation(*currentContract.contract, _type)));
|
doc->reset(new Json::Value(InterfaceHandler::documentation(*currentContract.contract, _type)));
|
||||||
|
|
||||||
return *(*doc);
|
return *(*doc);
|
||||||
}
|
}
|
||||||
@ -590,9 +591,19 @@ void CompilerStack::compileContract(
|
|||||||
compiledContract.runtimeObject = compiler->runtimeObject();
|
compiledContract.runtimeObject = compiler->runtimeObject();
|
||||||
_compiledContracts[compiledContract.contract] = &compiler->assembly();
|
_compiledContracts[compiledContract.contract] = &compiler->assembly();
|
||||||
|
|
||||||
Compiler cloneCompiler(_optimize, _runs);
|
try
|
||||||
cloneCompiler.compileClone(_contract, _compiledContracts);
|
{
|
||||||
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
Compiler cloneCompiler(_optimize, _runs);
|
||||||
|
cloneCompiler.compileClone(_contract, _compiledContracts);
|
||||||
|
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
||||||
|
}
|
||||||
|
catch (eth::AssemblyException const&)
|
||||||
|
{
|
||||||
|
// In some cases (if the constructor requests a runtime function), it is not
|
||||||
|
// possible to compile the clone.
|
||||||
|
|
||||||
|
// TODO: Report error / warning
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CompilerStack::defaultContractName() const
|
std::string CompilerStack::defaultContractName() const
|
||||||
|
@ -162,14 +162,14 @@ public:
|
|||||||
/// @returns a mapping assigning each source name its index inside the vector returned
|
/// @returns a mapping assigning each source name its index inside the vector returned
|
||||||
/// by sourceNames().
|
/// by sourceNames().
|
||||||
std::map<std::string, unsigned> sourceIndices() const;
|
std::map<std::string, unsigned> sourceIndices() const;
|
||||||
/// @returns a string representing the contract interface in JSON.
|
/// @returns a JSON representing the contract interface.
|
||||||
/// Prerequisite: Successful call to parse or compile.
|
/// Prerequisite: Successful call to parse or compile.
|
||||||
std::string const& interface(std::string const& _contractName = "") const;
|
Json::Value const& interface(std::string const& _contractName = "") const;
|
||||||
/// @returns a string representing the contract's documentation in JSON.
|
/// @returns a JSON representing the contract's documentation.
|
||||||
/// Prerequisite: Successful call to parse or compile.
|
/// Prerequisite: Successful call to parse or compile.
|
||||||
/// @param type The type of the documentation to get.
|
/// @param type The type of the documentation to get.
|
||||||
/// Can be one of 4 types defined at @c DocumentationType
|
/// Can be one of 4 types defined at @c DocumentationType
|
||||||
std::string const& metadata(std::string const& _contractName, DocumentationType _type) const;
|
Json::Value const& metadata(std::string const& _contractName, DocumentationType _type) const;
|
||||||
|
|
||||||
/// @returns the previously used scanner, useful for counting lines during error reporting.
|
/// @returns the previously used scanner, useful for counting lines during error reporting.
|
||||||
Scanner const& scanner(std::string const& _sourceName = "") const;
|
Scanner const& scanner(std::string const& _sourceName = "") const;
|
||||||
@ -213,9 +213,9 @@ private:
|
|||||||
eth::LinkerObject object;
|
eth::LinkerObject object;
|
||||||
eth::LinkerObject runtimeObject;
|
eth::LinkerObject runtimeObject;
|
||||||
eth::LinkerObject cloneObject;
|
eth::LinkerObject cloneObject;
|
||||||
mutable std::unique_ptr<std::string const> interface;
|
mutable std::unique_ptr<Json::Value const> interface;
|
||||||
mutable std::unique_ptr<std::string const> userDocumentation;
|
mutable std::unique_ptr<Json::Value const> userDocumentation;
|
||||||
mutable std::unique_ptr<std::string const> devDocumentation;
|
mutable std::unique_ptr<Json::Value const> devDocumentation;
|
||||||
mutable std::unique_ptr<std::string const> sourceMapping;
|
mutable std::unique_ptr<std::string const> sourceMapping;
|
||||||
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
|
mutable std::unique_ptr<std::string const> runtimeSourceMapping;
|
||||||
};
|
};
|
||||||
|
@ -37,6 +37,7 @@ using ErrorList = std::vector<std::shared_ptr<Error const>>;
|
|||||||
struct CompilerError: virtual Exception {};
|
struct CompilerError: virtual Exception {};
|
||||||
struct InternalCompilerError: virtual Exception {};
|
struct InternalCompilerError: virtual Exception {};
|
||||||
struct FatalError: virtual Exception {};
|
struct FatalError: virtual Exception {};
|
||||||
|
struct UnimplementedFeatureError: virtual Exception{};
|
||||||
|
|
||||||
class Error: virtual public Exception
|
class Error: virtual public Exception
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
|
|
||||||
string InterfaceHandler::documentation(
|
Json::Value InterfaceHandler::documentation(
|
||||||
ContractDefinition const& _contractDef,
|
ContractDefinition const& _contractDef,
|
||||||
DocumentationType _type
|
DocumentationType _type
|
||||||
)
|
)
|
||||||
@ -24,10 +24,9 @@ string InterfaceHandler::documentation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown documentation type"));
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
Json::Value InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
||||||
{
|
{
|
||||||
Json::Value abi(Json::arrayValue);
|
Json::Value abi(Json::arrayValue);
|
||||||
|
|
||||||
@ -67,8 +66,9 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
|||||||
{
|
{
|
||||||
Json::Value method;
|
Json::Value method;
|
||||||
method["type"] = "constructor";
|
method["type"] = "constructor";
|
||||||
auto externalFunction = FunctionType(*_contractDef.constructor()).interfaceFunctionType();
|
auto externalFunction = FunctionType(*_contractDef.constructor(), false).interfaceFunctionType();
|
||||||
solAssert(!!externalFunction, "");
|
solAssert(!!externalFunction, "");
|
||||||
|
method["payable"] = externalFunction->isPayable();
|
||||||
method["inputs"] = populateParameters(
|
method["inputs"] = populateParameters(
|
||||||
externalFunction->parameterNames(),
|
externalFunction->parameterNames(),
|
||||||
externalFunction->parameterTypeNames(_contractDef.isLibrary())
|
externalFunction->parameterTypeNames(_contractDef.isLibrary())
|
||||||
@ -77,7 +77,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
|||||||
}
|
}
|
||||||
if (_contractDef.fallbackFunction())
|
if (_contractDef.fallbackFunction())
|
||||||
{
|
{
|
||||||
auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction()).interfaceFunctionType();
|
auto externalFunctionType = FunctionType(*_contractDef.fallbackFunction(), false).interfaceFunctionType();
|
||||||
solAssert(!!externalFunctionType, "");
|
solAssert(!!externalFunctionType, "");
|
||||||
Json::Value method;
|
Json::Value method;
|
||||||
method["type"] = "fallback";
|
method["type"] = "fallback";
|
||||||
@ -103,10 +103,11 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
|||||||
event["inputs"] = params;
|
event["inputs"] = params;
|
||||||
abi.append(event);
|
abi.append(event);
|
||||||
}
|
}
|
||||||
return Json::FastWriter().write(abi);
|
|
||||||
|
return abi;
|
||||||
}
|
}
|
||||||
|
|
||||||
string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
|
Json::Value InterfaceHandler::userDocumentation(ContractDefinition const& _contractDef)
|
||||||
{
|
{
|
||||||
Json::Value doc;
|
Json::Value doc;
|
||||||
Json::Value methods(Json::objectValue);
|
Json::Value methods(Json::objectValue);
|
||||||
@ -126,10 +127,10 @@ string InterfaceHandler::userDocumentation(ContractDefinition const& _contractDe
|
|||||||
}
|
}
|
||||||
doc["methods"] = methods;
|
doc["methods"] = methods;
|
||||||
|
|
||||||
return Json::StyledWriter().write(doc);
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
|
Json::Value InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef)
|
||||||
{
|
{
|
||||||
Json::Value doc;
|
Json::Value doc;
|
||||||
Json::Value methods(Json::objectValue);
|
Json::Value methods(Json::objectValue);
|
||||||
@ -175,7 +176,7 @@ string InterfaceHandler::devDocumentation(ContractDefinition const& _contractDef
|
|||||||
}
|
}
|
||||||
doc["methods"] = methods;
|
doc["methods"] = methods;
|
||||||
|
|
||||||
return Json::StyledWriter().write(doc);
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
|
string InterfaceHandler::extractDoc(multimap<string, DocTag> const& _tags, string const& _name)
|
||||||
|
@ -64,24 +64,24 @@ public:
|
|||||||
/// @param _contractDef The contract definition
|
/// @param _contractDef The contract definition
|
||||||
/// @param _type The type of the documentation. Can be one of the
|
/// @param _type The type of the documentation. Can be one of the
|
||||||
/// types provided by @c DocumentationType
|
/// types provided by @c DocumentationType
|
||||||
/// @return A string with the json representation of provided type
|
/// @return A JSON representation of provided type
|
||||||
static std::string documentation(
|
static Json::Value documentation(
|
||||||
ContractDefinition const& _contractDef,
|
ContractDefinition const& _contractDef,
|
||||||
DocumentationType _type
|
DocumentationType _type
|
||||||
);
|
);
|
||||||
/// Get the ABI Interface of the contract
|
/// Get the ABI Interface of the contract
|
||||||
/// @param _contractDef The contract definition
|
/// @param _contractDef The contract definition
|
||||||
/// @return A string with the json representation of the contract's ABI Interface
|
/// @return A JSONrepresentation of the contract's ABI Interface
|
||||||
static std::string abiInterface(ContractDefinition const& _contractDef);
|
static Json::Value abiInterface(ContractDefinition const& _contractDef);
|
||||||
/// Get the User documentation of the contract
|
/// Get the User documentation of the contract
|
||||||
/// @param _contractDef The contract definition
|
/// @param _contractDef The contract definition
|
||||||
/// @return A string with the json representation of the contract's user documentation
|
/// @return A JSON representation of the contract's user documentation
|
||||||
static std::string userDocumentation(ContractDefinition const& _contractDef);
|
static Json::Value userDocumentation(ContractDefinition const& _contractDef);
|
||||||
/// Genereates the Developer's documentation of the contract
|
/// Genereates the Developer's documentation of the contract
|
||||||
/// @param _contractDef The contract definition
|
/// @param _contractDef The contract definition
|
||||||
/// @return A string with the json representation
|
/// @return A JSON representation
|
||||||
/// of the contract's developer documentation
|
/// of the contract's developer documentation
|
||||||
static std::string devDocumentation(ContractDefinition const& _contractDef);
|
static Json::Value devDocumentation(ContractDefinition const& _contractDef);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// @returns concatenation of all content under the given tag name.
|
/// @returns concatenation of all content under the given tag name.
|
||||||
|
@ -30,6 +30,7 @@ namespace dev
|
|||||||
namespace solidity
|
namespace solidity
|
||||||
{
|
{
|
||||||
struct InternalCompilerError;
|
struct InternalCompilerError;
|
||||||
|
struct UnimplementedFeatureError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,3 +38,8 @@ struct InternalCompilerError;
|
|||||||
#define solAssert(CONDITION, DESCRIPTION) \
|
#define solAssert(CONDITION, DESCRIPTION) \
|
||||||
assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
|
assertThrow(CONDITION, ::dev::solidity::InternalCompilerError, DESCRIPTION)
|
||||||
|
|
||||||
|
#define solUnimplementedAssert(CONDITION, DESCRIPTION) \
|
||||||
|
assertThrow(CONDITION, ::dev::solidity::UnimplementedFeatureError, DESCRIPTION)
|
||||||
|
|
||||||
|
#define solUnimplemented(DESCRIPTION) \
|
||||||
|
solUnimplementedAssert(false, DESCRIPTION)
|
||||||
|
@ -217,7 +217,9 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
|
|||||||
if (currentTokenValue == Token::RBrace)
|
if (currentTokenValue == Token::RBrace)
|
||||||
break;
|
break;
|
||||||
else if (currentTokenValue == Token::Function)
|
else if (currentTokenValue == Token::Function)
|
||||||
subNodes.push_back(parseFunctionDefinition(name.get()));
|
// This can be a function or a state variable of function type (especially
|
||||||
|
// complicated to distinguish fallback function from function type state variable)
|
||||||
|
subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get()));
|
||||||
else if (currentTokenValue == Token::Struct)
|
else if (currentTokenValue == Token::Struct)
|
||||||
subNodes.push_back(parseStructDefinition());
|
subNodes.push_back(parseStructDefinition());
|
||||||
else if (currentTokenValue == Token::Enum)
|
else if (currentTokenValue == Token::Enum)
|
||||||
@ -288,81 +290,121 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
|
|||||||
return visibility;
|
return visibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(ASTString const* _contractName)
|
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
|
||||||
|
{
|
||||||
|
FunctionHeaderParserResult result;
|
||||||
|
expectToken(Token::Function);
|
||||||
|
if (_forceEmptyName || m_scanner->currentToken() == Token::LParen)
|
||||||
|
result.name = make_shared<ASTString>(); // anonymous function
|
||||||
|
else
|
||||||
|
result.name = expectIdentifierToken();
|
||||||
|
VarDeclParserOptions options;
|
||||||
|
options.allowLocationSpecifier = true;
|
||||||
|
result.parameters = parseParameterList(options);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Token::Value token = m_scanner->currentToken();
|
||||||
|
if (token == Token::Const)
|
||||||
|
{
|
||||||
|
result.isDeclaredConst = true;
|
||||||
|
m_scanner->next();
|
||||||
|
}
|
||||||
|
else if (m_scanner->currentToken() == Token::Payable)
|
||||||
|
{
|
||||||
|
result.isPayable = true;
|
||||||
|
m_scanner->next();
|
||||||
|
}
|
||||||
|
else if (_allowModifiers && token == Token::Identifier)
|
||||||
|
{
|
||||||
|
// This can either be a modifier (function declaration) or the name of the
|
||||||
|
// variable (function type name plus variable).
|
||||||
|
if (
|
||||||
|
m_scanner->peekNextToken() == Token::Semicolon ||
|
||||||
|
m_scanner->peekNextToken() == Token::Assign
|
||||||
|
)
|
||||||
|
// Variable declaration, break here.
|
||||||
|
break;
|
||||||
|
else
|
||||||
|
result.modifiers.push_back(parseModifierInvocation());
|
||||||
|
}
|
||||||
|
else if (Token::isVisibilitySpecifier(token))
|
||||||
|
{
|
||||||
|
if (result.visibility != Declaration::Visibility::Default)
|
||||||
|
fatalParserError(string("Multiple visibility specifiers."));
|
||||||
|
result.visibility = parseVisibilitySpecifier(token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (m_scanner->currentToken() == Token::Returns)
|
||||||
|
{
|
||||||
|
bool const permitEmptyParameterList = false;
|
||||||
|
m_scanner->next();
|
||||||
|
result.returnParameters = parseParameterList(options, permitEmptyParameterList);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result.returnParameters = createEmptyParameterList();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
ASTPointer<ASTString> docstring;
|
ASTPointer<ASTString> docstring;
|
||||||
if (m_scanner->currentCommentLiteral() != "")
|
if (m_scanner->currentCommentLiteral() != "")
|
||||||
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
|
||||||
|
|
||||||
expectToken(Token::Function);
|
FunctionHeaderParserResult header = parseFunctionHeader(false, true);
|
||||||
ASTPointer<ASTString> name;
|
|
||||||
if (m_scanner->currentToken() == Token::LParen)
|
if (
|
||||||
name = make_shared<ASTString>(); // anonymous function
|
!header.modifiers.empty() ||
|
||||||
else
|
!header.name->empty() ||
|
||||||
name = expectIdentifierToken();
|
m_scanner->currentToken() == Token::Semicolon ||
|
||||||
VarDeclParserOptions options;
|
m_scanner->currentToken() == Token::LBrace
|
||||||
options.allowLocationSpecifier = true;
|
)
|
||||||
ASTPointer<ParameterList> parameters(parseParameterList(options));
|
|
||||||
bool isDeclaredConst = false;
|
|
||||||
bool isPayable = false;
|
|
||||||
Declaration::Visibility visibility(Declaration::Visibility::Default);
|
|
||||||
vector<ASTPointer<ModifierInvocation>> modifiers;
|
|
||||||
while (true)
|
|
||||||
{
|
{
|
||||||
Token::Value token = m_scanner->currentToken();
|
// this has to be a function
|
||||||
if (token == Token::Const)
|
ASTPointer<Block> block = ASTPointer<Block>();
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
if (m_scanner->currentToken() != Token::Semicolon)
|
||||||
{
|
{
|
||||||
isDeclaredConst = true;
|
block = parseBlock();
|
||||||
m_scanner->next();
|
nodeFactory.setEndPositionFromNode(block);
|
||||||
}
|
|
||||||
else if (m_scanner->currentToken() == Token::Payable)
|
|
||||||
{
|
|
||||||
isPayable = true;
|
|
||||||
m_scanner->next();
|
|
||||||
}
|
|
||||||
else if (token == Token::Identifier)
|
|
||||||
modifiers.push_back(parseModifierInvocation());
|
|
||||||
else if (Token::isVisibilitySpecifier(token))
|
|
||||||
{
|
|
||||||
if (visibility != Declaration::Visibility::Default)
|
|
||||||
fatalParserError(string("Multiple visibility specifiers."));
|
|
||||||
visibility = parseVisibilitySpecifier(token);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
break;
|
m_scanner->next(); // just consume the ';'
|
||||||
}
|
bool const c_isConstructor = (_contractName && *header.name == *_contractName);
|
||||||
ASTPointer<ParameterList> returnParameters;
|
return nodeFactory.createNode<FunctionDefinition>(
|
||||||
if (m_scanner->currentToken() == Token::Returns)
|
header.name,
|
||||||
{
|
header.visibility,
|
||||||
bool const permitEmptyParameterList = false;
|
c_isConstructor,
|
||||||
m_scanner->next();
|
docstring,
|
||||||
returnParameters = parseParameterList(options, permitEmptyParameterList);
|
header.parameters,
|
||||||
|
header.isDeclaredConst,
|
||||||
|
header.modifiers,
|
||||||
|
header.returnParameters,
|
||||||
|
header.isPayable,
|
||||||
|
block
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
returnParameters = createEmptyParameterList();
|
|
||||||
ASTPointer<Block> block = ASTPointer<Block>();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
if (m_scanner->currentToken() != Token::Semicolon)
|
|
||||||
{
|
{
|
||||||
block = parseBlock();
|
// this has to be a state variable
|
||||||
nodeFactory.setEndPositionFromNode(block);
|
ASTPointer<TypeName> type = nodeFactory.createNode<FunctionTypeName>(
|
||||||
|
header.parameters,
|
||||||
|
header.returnParameters,
|
||||||
|
header.visibility,
|
||||||
|
header.isDeclaredConst,
|
||||||
|
header.isPayable
|
||||||
|
);
|
||||||
|
type = parseTypeNameSuffix(type, nodeFactory);
|
||||||
|
VarDeclParserOptions options;
|
||||||
|
options.isStateVariable = true;
|
||||||
|
options.allowInitialValue = true;
|
||||||
|
auto node = parseVariableDeclaration(options, type);
|
||||||
|
expectToken(Token::Semicolon);
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
m_scanner->next(); // just consume the ';'
|
|
||||||
bool const c_isConstructor = (_contractName && *name == *_contractName);
|
|
||||||
return nodeFactory.createNode<FunctionDefinition>(
|
|
||||||
name,
|
|
||||||
visibility,
|
|
||||||
c_isConstructor,
|
|
||||||
docstring,
|
|
||||||
parameters,
|
|
||||||
isDeclaredConst,
|
|
||||||
modifiers,
|
|
||||||
returnParameters,
|
|
||||||
isPayable,
|
|
||||||
block
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ASTPointer<StructDefinition> Parser::parseStructDefinition()
|
ASTPointer<StructDefinition> Parser::parseStructDefinition()
|
||||||
@ -406,6 +448,8 @@ ASTPointer<EnumDefinition> Parser::parseEnumDefinition()
|
|||||||
if (m_scanner->currentToken() != Token::Identifier)
|
if (m_scanner->currentToken() != Token::Identifier)
|
||||||
fatalParserError(string("Expected Identifier after ','"));
|
fatalParserError(string("Expected Identifier after ','"));
|
||||||
}
|
}
|
||||||
|
if (members.size() == 0)
|
||||||
|
parserError({"enum with no members is not allowed."});
|
||||||
|
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
expectToken(Token::RBrace);
|
expectToken(Token::RBrace);
|
||||||
@ -609,6 +653,21 @@ ASTPointer<UserDefinedTypeName> Parser::parseUserDefinedTypeName()
|
|||||||
return nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
|
return nodeFactory.createNode<UserDefinedTypeName>(identifierPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTPointer<TypeName> Parser::parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory)
|
||||||
|
{
|
||||||
|
while (m_scanner->currentToken() == Token::LBrack)
|
||||||
|
{
|
||||||
|
m_scanner->next();
|
||||||
|
ASTPointer<Expression> length;
|
||||||
|
if (m_scanner->currentToken() != Token::RBrack)
|
||||||
|
length = parseExpression();
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
expectToken(Token::RBrack);
|
||||||
|
type = nodeFactory.createNode<ArrayTypeName>(type, length);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
@ -629,6 +688,8 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|||||||
fatalParserError(string("Expected explicit type name."));
|
fatalParserError(string("Expected explicit type name."));
|
||||||
m_scanner->next();
|
m_scanner->next();
|
||||||
}
|
}
|
||||||
|
else if (token == Token::Function)
|
||||||
|
type = parseFunctionType();
|
||||||
else if (token == Token::Mapping)
|
else if (token == Token::Mapping)
|
||||||
type = parseMapping();
|
type = parseMapping();
|
||||||
else if (token == Token::Identifier)
|
else if (token == Token::Identifier)
|
||||||
@ -638,19 +699,23 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
|
|||||||
|
|
||||||
if (type)
|
if (type)
|
||||||
// Parse "[...]" postfixes for arrays.
|
// Parse "[...]" postfixes for arrays.
|
||||||
while (m_scanner->currentToken() == Token::LBrack)
|
type = parseTypeNameSuffix(type, nodeFactory);
|
||||||
{
|
|
||||||
m_scanner->next();
|
|
||||||
ASTPointer<Expression> length;
|
|
||||||
if (m_scanner->currentToken() != Token::RBrack)
|
|
||||||
length = parseExpression();
|
|
||||||
nodeFactory.markEndPosition();
|
|
||||||
expectToken(Token::RBrack);
|
|
||||||
type = nodeFactory.createNode<ArrayTypeName>(type, length);
|
|
||||||
}
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTPointer<FunctionTypeName> Parser::parseFunctionType()
|
||||||
|
{
|
||||||
|
ASTNodeFactory nodeFactory(*this);
|
||||||
|
FunctionHeaderParserResult header = parseFunctionHeader(true, false);
|
||||||
|
return nodeFactory.createNode<FunctionTypeName>(
|
||||||
|
header.parameters,
|
||||||
|
header.returnParameters,
|
||||||
|
header.visibility,
|
||||||
|
header.isDeclaredConst,
|
||||||
|
header.isPayable
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ASTPointer<Mapping> Parser::parseMapping()
|
ASTPointer<Mapping> Parser::parseMapping()
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
@ -722,6 +787,8 @@ ASTPointer<Statement> Parser::parseStatement()
|
|||||||
return parseIfStatement(docString);
|
return parseIfStatement(docString);
|
||||||
case Token::While:
|
case Token::While:
|
||||||
return parseWhileStatement(docString);
|
return parseWhileStatement(docString);
|
||||||
|
case Token::Do:
|
||||||
|
return parseDoWhileStatement(docString);
|
||||||
case Token::For:
|
case Token::For:
|
||||||
return parseForStatement(docString);
|
return parseForStatement(docString);
|
||||||
case Token::LBrace:
|
case Token::LBrace:
|
||||||
@ -816,9 +883,24 @@ ASTPointer<WhileStatement> Parser::parseWhileStatement(ASTPointer<ASTString> con
|
|||||||
expectToken(Token::RParen);
|
expectToken(Token::RParen);
|
||||||
ASTPointer<Statement> body = parseStatement();
|
ASTPointer<Statement> body = parseStatement();
|
||||||
nodeFactory.setEndPositionFromNode(body);
|
nodeFactory.setEndPositionFromNode(body);
|
||||||
return nodeFactory.createNode<WhileStatement>(_docString, condition, body);
|
return nodeFactory.createNode<WhileStatement>(_docString, condition, body, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASTPointer<WhileStatement> Parser::parseDoWhileStatement(ASTPointer<ASTString> const& _docString)
|
||||||
|
{
|
||||||
|
ASTNodeFactory nodeFactory(*this);
|
||||||
|
expectToken(Token::Do);
|
||||||
|
ASTPointer<Statement> body = parseStatement();
|
||||||
|
expectToken(Token::While);
|
||||||
|
expectToken(Token::LParen);
|
||||||
|
ASTPointer<Expression> condition = parseExpression();
|
||||||
|
expectToken(Token::RParen);
|
||||||
|
nodeFactory.markEndPosition();
|
||||||
|
expectToken(Token::Semicolon);
|
||||||
|
return nodeFactory.createNode<WhileStatement>(_docString, condition, body, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString)
|
ASTPointer<ForStatement> Parser::parseForStatement(ASTPointer<ASTString> const& _docString)
|
||||||
{
|
{
|
||||||
ASTNodeFactory nodeFactory(*this);
|
ASTNodeFactory nodeFactory(*this);
|
||||||
@ -1259,7 +1341,7 @@ Parser::LookAheadInfo Parser::peekStatementType() const
|
|||||||
Token::Value token(m_scanner->currentToken());
|
Token::Value token(m_scanner->currentToken());
|
||||||
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
|
bool mightBeTypeName = (Token::isElementaryTypeName(token) || token == Token::Identifier);
|
||||||
|
|
||||||
if (token == Token::Mapping || token == Token::Var)
|
if (token == Token::Mapping || token == Token::Function || token == Token::Var)
|
||||||
return LookAheadInfo::VariableDeclarationStatement;
|
return LookAheadInfo::VariableDeclarationStatement;
|
||||||
if (mightBeTypeName)
|
if (mightBeTypeName)
|
||||||
{
|
{
|
||||||
|
@ -53,6 +53,18 @@ private:
|
|||||||
bool allowLocationSpecifier = false;
|
bool allowLocationSpecifier = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This struct is shared for parsing a function header and a function type.
|
||||||
|
struct FunctionHeaderParserResult
|
||||||
|
{
|
||||||
|
ASTPointer<ASTString> name;
|
||||||
|
ASTPointer<ParameterList> parameters;
|
||||||
|
ASTPointer<ParameterList> returnParameters;
|
||||||
|
Declaration::Visibility visibility = Declaration::Visibility::Default;
|
||||||
|
bool isDeclaredConst = false;
|
||||||
|
bool isPayable = false;
|
||||||
|
std::vector<ASTPointer<ModifierInvocation>> modifiers;
|
||||||
|
};
|
||||||
|
|
||||||
///@{
|
///@{
|
||||||
///@name Parsing functions for the AST nodes
|
///@name Parsing functions for the AST nodes
|
||||||
ASTPointer<PragmaDirective> parsePragmaDirective();
|
ASTPointer<PragmaDirective> parsePragmaDirective();
|
||||||
@ -60,6 +72,8 @@ private:
|
|||||||
ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
|
ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
|
||||||
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
|
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
|
||||||
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
|
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
|
||||||
|
FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
|
||||||
|
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
|
||||||
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
|
||||||
ASTPointer<StructDefinition> parseStructDefinition();
|
ASTPointer<StructDefinition> parseStructDefinition();
|
||||||
ASTPointer<EnumDefinition> parseEnumDefinition();
|
ASTPointer<EnumDefinition> parseEnumDefinition();
|
||||||
@ -74,7 +88,9 @@ private:
|
|||||||
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
ASTPointer<ModifierInvocation> parseModifierInvocation();
|
||||||
ASTPointer<Identifier> parseIdentifier();
|
ASTPointer<Identifier> parseIdentifier();
|
||||||
ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName();
|
ASTPointer<UserDefinedTypeName> parseUserDefinedTypeName();
|
||||||
|
ASTPointer<TypeName> parseTypeNameSuffix(ASTPointer<TypeName> type, ASTNodeFactory& nodeFactory);
|
||||||
ASTPointer<TypeName> parseTypeName(bool _allowVar);
|
ASTPointer<TypeName> parseTypeName(bool _allowVar);
|
||||||
|
ASTPointer<FunctionTypeName> parseFunctionType();
|
||||||
ASTPointer<Mapping> parseMapping();
|
ASTPointer<Mapping> parseMapping();
|
||||||
ASTPointer<ParameterList> parseParameterList(
|
ASTPointer<ParameterList> parseParameterList(
|
||||||
VarDeclParserOptions const& _options,
|
VarDeclParserOptions const& _options,
|
||||||
@ -85,6 +101,7 @@ private:
|
|||||||
ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {});
|
ASTPointer<InlineAssembly> parseInlineAssembly(ASTPointer<ASTString> const& _docString = {});
|
||||||
ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<IfStatement> parseIfStatement(ASTPointer<ASTString> const& _docString);
|
||||||
ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<WhileStatement> parseWhileStatement(ASTPointer<ASTString> const& _docString);
|
||||||
|
ASTPointer<WhileStatement> parseDoWhileStatement(ASTPointer<ASTString> const& _docString);
|
||||||
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<ForStatement> parseForStatement(ASTPointer<ASTString> const& _docString);
|
||||||
/// A "simple statement" can be a variable declaration statement or an expression statement.
|
/// A "simple statement" can be a variable declaration statement or an expression statement.
|
||||||
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
|
ASTPointer<Statement> parseSimpleStatement(ASTPointer<ASTString> const& _docString);
|
||||||
|
@ -27,11 +27,18 @@
|
|||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
|
#include <solidity/BuildInfo.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::solidity;
|
using namespace dev::solidity;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
|
|
||||||
|
static string const VersionString =
|
||||||
|
string(ETH_PROJECT_VERSION) +
|
||||||
|
(string(SOL_VERSION_PRERELEASE).empty() ? "" : "-" + string(SOL_VERSION_PRERELEASE)) +
|
||||||
|
(string(SOL_VERSION_BUILDINFO).empty() ? "" : "+" + string(SOL_VERSION_BUILDINFO));
|
||||||
|
|
||||||
void help()
|
void help()
|
||||||
{
|
{
|
||||||
cout
|
cout
|
||||||
@ -50,7 +57,7 @@ void help()
|
|||||||
void version()
|
void version()
|
||||||
{
|
{
|
||||||
cout << "LLLC, the Lovely Little Language Compiler " << endl;
|
cout << "LLLC, the Lovely Little Language Compiler " << endl;
|
||||||
cout << " By Gav Wood, (c) 2014." << endl;
|
cout << "Version: " << VersionString << endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,6 @@ case $(uname -s) in
|
|||||||
|
|
||||||
brew install boost
|
brew install boost
|
||||||
brew install cmake
|
brew install cmake
|
||||||
brew install jsoncpp
|
|
||||||
|
|
||||||
# We should really 'brew install' our eth client here, but at the time of writing
|
# We should really 'brew install' our eth client here, but at the time of writing
|
||||||
# the bottle is known broken, so we will just cheat and use a hardcoded ZIP for
|
# the bottle is known broken, so we will just cheat and use a hardcoded ZIP for
|
||||||
@ -164,7 +163,7 @@ case $(uname -s) in
|
|||||||
# See https://pkgs.alpinelinux.org/
|
# See https://pkgs.alpinelinux.org/
|
||||||
|
|
||||||
apk update
|
apk update
|
||||||
apk add boost-dev build-base cmake jsoncpp-dev
|
apk add boost-dev build-base cmake
|
||||||
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -219,7 +218,6 @@ case $(uname -s) in
|
|||||||
gcc \
|
gcc \
|
||||||
git \
|
git \
|
||||||
libboost-all-dev \
|
libboost-all-dev \
|
||||||
libjsoncpp-dev \
|
|
||||||
unzip
|
unzip
|
||||||
|
|
||||||
;;
|
;;
|
||||||
@ -321,8 +319,7 @@ case $(uname -s) in
|
|||||||
build-essential \
|
build-essential \
|
||||||
cmake \
|
cmake \
|
||||||
git \
|
git \
|
||||||
libboost-all-dev \
|
libboost-all-dev
|
||||||
libjsoncpp-dev
|
|
||||||
|
|
||||||
# Install 'eth', for use in the Solidity Tests-over-IPC.
|
# Install 'eth', for use in the Solidity Tests-over-IPC.
|
||||||
sudo add-apt-repository -y ppa:ethereum/ethereum
|
sudo add-apt-repository -y ppa:ethereum/ethereum
|
||||||
@ -363,9 +360,6 @@ case $(uname -s) in
|
|||||||
sudo yum -y remove boost-devel
|
sudo yum -y remove boost-devel
|
||||||
sudo wget http://repo.enetres.net/enetres.repo -O /etc/yum.repos.d/enetres.repo
|
sudo wget http://repo.enetres.net/enetres.repo -O /etc/yum.repos.d/enetres.repo
|
||||||
sudo yum install boost-devel
|
sudo yum install boost-devel
|
||||||
|
|
||||||
# And finally jsoncpp
|
|
||||||
sudo yum -y install jsoncpp-devel
|
|
||||||
else
|
else
|
||||||
echo "Aborted CentOS Solidity Dependency Installation";
|
echo "Aborted CentOS Solidity Dependency Installation";
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -49,6 +49,10 @@ cd $distribution
|
|||||||
git clone --recursive https://github.com/ethereum/solidity.git -b "$branch"
|
git clone --recursive https://github.com/ethereum/solidity.git -b "$branch"
|
||||||
mv solidity solc
|
mv solidity solc
|
||||||
|
|
||||||
|
# Fetch jsoncpp dependency
|
||||||
|
mkdir -p ./solc/deps/downloads/ 2>/dev/null || true
|
||||||
|
wget -O ./solc/deps/downloads/jsoncpp-1.7.7.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.7.7.tar.gz
|
||||||
|
|
||||||
# Determine version
|
# Determine version
|
||||||
cd solc
|
cd solc
|
||||||
version=`grep -oP "PROJECT_VERSION \"?\K[0-9.]+(?=\")"? CMakeLists.txt`
|
version=`grep -oP "PROJECT_VERSION \"?\K[0-9.]+(?=\")"? CMakeLists.txt`
|
||||||
@ -76,21 +80,6 @@ cp /tmp/${packagename}_${debversion}.orig.tar.gz ../
|
|||||||
|
|
||||||
# Create debian package information
|
# Create debian package information
|
||||||
|
|
||||||
case $distribution in
|
|
||||||
trusty)
|
|
||||||
jsoncpplib=libjsoncpp0
|
|
||||||
;;
|
|
||||||
vivid)
|
|
||||||
jsoncpplib=libjsoncpp0
|
|
||||||
;;
|
|
||||||
wily)
|
|
||||||
jsoncpplib=libjsoncpp0v5
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
jsoncpplib=libjsoncpp1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
mkdir debian
|
mkdir debian
|
||||||
echo 9 > debian/compat
|
echo 9 > debian/compat
|
||||||
cat <<EOF > debian/control
|
cat <<EOF > debian/control
|
||||||
@ -99,7 +88,6 @@ Section: science
|
|||||||
Priority: extra
|
Priority: extra
|
||||||
Maintainer: Christian (Buildserver key) <builds@ethereum.org>
|
Maintainer: Christian (Buildserver key) <builds@ethereum.org>
|
||||||
Build-Depends: debhelper (>= 9.0.0),
|
Build-Depends: debhelper (>= 9.0.0),
|
||||||
libcryptopp-dev,
|
|
||||||
cmake,
|
cmake,
|
||||||
g++-4.8,
|
g++-4.8,
|
||||||
git,
|
git,
|
||||||
@ -107,8 +95,7 @@ Build-Depends: debhelper (>= 9.0.0),
|
|||||||
libboost-all-dev,
|
libboost-all-dev,
|
||||||
automake,
|
automake,
|
||||||
libtool,
|
libtool,
|
||||||
scons,
|
scons
|
||||||
libjsoncpp-dev
|
|
||||||
Standards-Version: 3.9.5
|
Standards-Version: 3.9.5
|
||||||
Homepage: https://ethereum.org
|
Homepage: https://ethereum.org
|
||||||
Vcs-Git: git://github.com/ethereum/solidity.git
|
Vcs-Git: git://github.com/ethereum/solidity.git
|
||||||
@ -117,7 +104,7 @@ Vcs-Browser: https://github.com/ethereum/solidity
|
|||||||
Package: solc
|
Package: solc
|
||||||
Architecture: any-i386 any-amd64
|
Architecture: any-i386 any-amd64
|
||||||
Multi-Arch: same
|
Multi-Arch: same
|
||||||
Depends: \${shlibs:Depends}, \${misc:Depends}, $jsoncpplib
|
Depends: \${shlibs:Depends}, \${misc:Depends}
|
||||||
Replaces: lllc (<< 1:0.3.6)
|
Replaces: lllc (<< 1:0.3.6)
|
||||||
Conflicts: libethereum (<= 1.2.9)
|
Conflicts: libethereum (<= 1.2.9)
|
||||||
Description: Solidity compiler.
|
Description: Solidity compiler.
|
||||||
|
@ -28,8 +28,22 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# There is an implicit assumption here that we HAVE to run from root directory.
|
REPO_ROOT="$(dirname "$0")"/..
|
||||||
REPO_ROOT=$(pwd)
|
|
||||||
|
# Compile all files in std and examples.
|
||||||
|
|
||||||
|
for f in "$REPO_ROOT"/std/*.sol
|
||||||
|
do
|
||||||
|
echo "Compiling $f..."
|
||||||
|
set +e
|
||||||
|
output=$("$REPO_ROOT"/build/solc/solc "$f" 2>&1)
|
||||||
|
failed=$?
|
||||||
|
# Remove the pre-release warning from the compiler output
|
||||||
|
output=$(echo "$output" | grep -v 'pre-release')
|
||||||
|
echo "$output"
|
||||||
|
set -e
|
||||||
|
test -z "$output" -a "$failed" -eq 0
|
||||||
|
done
|
||||||
|
|
||||||
# This conditional is only needed because we don't have a working Homebrew
|
# This conditional is only needed because we don't have a working Homebrew
|
||||||
# install for `eth` at the time of writing, so we unzip the ZIP file locally
|
# install for `eth` at the time of writing, so we unzip the ZIP file locally
|
||||||
|
@ -40,32 +40,6 @@ apt-get -y install git-core
|
|||||||
|
|
||||||
export WORKSPACE=/src
|
export WORKSPACE=/src
|
||||||
|
|
||||||
# CryptoPP
|
|
||||||
echo -en 'travis_fold:start:compiling_cryptopp\\r'
|
|
||||||
cd "$WORKSPACE/cryptopp"
|
|
||||||
# if .git exists, it is a fresh checkout, otherwise it comes from the cache
|
|
||||||
# and is already compiled
|
|
||||||
test -e .git && (
|
|
||||||
emcmake cmake -DCRYPTOPP_LIBRARY_TYPE=STATIC -DCRYPTOPP_RUNTIME_TYPE=STATIC && emmake make -j 4
|
|
||||||
ln -s . src/cryptopp || true
|
|
||||||
rm -rf .git
|
|
||||||
)
|
|
||||||
echo -en 'travis_fold:end:compiling_cryptopp\\r'
|
|
||||||
|
|
||||||
# Json-CPP
|
|
||||||
echo -en 'travis_fold:start:compiling_jsoncpp\\r'
|
|
||||||
cd "$WORKSPACE/jsoncpp"
|
|
||||||
# if .git exists, it is a fresh checkout, otherwise it comes from the cache
|
|
||||||
# and is already compiled
|
|
||||||
test -e .git && (
|
|
||||||
emcmake cmake -DJSONCPP_LIB_BUILD_STATIC=ON -DJSONCPP_LIB_BUILD_SHARED=OFF \
|
|
||||||
-DJSONCPP_WITH_TESTS=OFF -DJSONCPP_WITH_POST_BUILD_UNITTEST=OFF \
|
|
||||||
-G "Unix Makefiles" .
|
|
||||||
emmake make -j 4
|
|
||||||
rm -rf .git
|
|
||||||
)
|
|
||||||
echo -en 'travis_fold:end:compiling_jsoncpp\\r'
|
|
||||||
|
|
||||||
# Boost
|
# Boost
|
||||||
echo -en 'travis_fold:start:compiling_boost\\r'
|
echo -en 'travis_fold:start:compiling_boost\\r'
|
||||||
cd "$WORKSPACE"/boost_1_57_0
|
cd "$WORKSPACE"/boost_1_57_0
|
||||||
@ -112,10 +86,6 @@ emcmake cmake \
|
|||||||
-DBoost_THREAD_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_thread.a \
|
-DBoost_THREAD_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_thread.a \
|
||||||
-DBoost_UNIT_TEST_FRAMEWORK_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \
|
-DBoost_UNIT_TEST_FRAMEWORK_LIBRARY="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \
|
||||||
-DBoost_UNIT_TEST_FRAMEWORK_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \
|
-DBoost_UNIT_TEST_FRAMEWORK_LIBRARIES="$WORKSPACE"/boost_1_57_0/libboost_unit_test_framework.a \
|
||||||
-DJSONCPP_LIBRARY="$WORKSPACE"/jsoncpp/src/lib_json/libjsoncpp.a \
|
|
||||||
-DJSONCPP_INCLUDE_DIR="$WORKSPACE"/jsoncpp/include/ \
|
|
||||||
-DCRYPTOPP_LIBRARY="$WORKSPACE"/cryptopp/src/libcryptlib.a \
|
|
||||||
-DCRYPTOPP_INCLUDE_DIR="$WORKSPACE"/cryptopp/src/ \
|
|
||||||
-DDev_DEVCORE_LIBRARY="$WORKSPACE"/solidity/build/libdevcore/libsoldevcore.a \
|
-DDev_DEVCORE_LIBRARY="$WORKSPACE"/solidity/build/libdevcore/libsoldevcore.a \
|
||||||
-DEth_EVMASM_LIBRARY="$WORKSPACE"/solidity/build/libevmasm/libsolevmasm.a \
|
-DEth_EVMASM_LIBRARY="$WORKSPACE"/solidity/build/libevmasm/libsolevmasm.a \
|
||||||
-DETH_STATIC=1 -DTESTS=0 \
|
-DETH_STATIC=1 -DTESTS=0 \
|
||||||
|
@ -30,8 +30,6 @@
|
|||||||
set -ev
|
set -ev
|
||||||
|
|
||||||
echo -en 'travis_fold:start:installing_dependencies\\r'
|
echo -en 'travis_fold:start:installing_dependencies\\r'
|
||||||
test -e cryptopp -a -e cryptopp/src || git clone https://github.com/mmoss/cryptopp.git
|
|
||||||
test -e jsoncpp -a -e jsoncpp/include || git clone https://github.com/open-source-parsers/jsoncpp.git
|
|
||||||
test -e boost_1_57_0 -a -e boost_1_57_0/boost || (
|
test -e boost_1_57_0 -a -e boost_1_57_0/boost || (
|
||||||
wget 'http://downloads.sourceforge.net/project/boost/boost/'\
|
wget 'http://downloads.sourceforge.net/project/boost/boost/'\
|
||||||
'1.57.0/boost_1_57_0.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2F'\
|
'1.57.0/boost_1_57_0.tar.bz2?r=http%3A%2F%2Fsourceforge.net%2F'\
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
|
#include <libdevcore/JSON.h>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/GasMeter.h>
|
#include <libevmasm/GasMeter.h>
|
||||||
#include <libsolidity/interface/Version.h>
|
#include <libsolidity/interface/Version.h>
|
||||||
@ -230,12 +231,18 @@ void CommandLineInterface::handleMeta(DocumentationType _type, string const& _co
|
|||||||
|
|
||||||
if (m_args.count(argName))
|
if (m_args.count(argName))
|
||||||
{
|
{
|
||||||
|
std::string output;
|
||||||
|
if (_type == DocumentationType::ABIInterface)
|
||||||
|
output = dev::jsonCompactPrint(m_compiler->metadata(_contract, _type));
|
||||||
|
else
|
||||||
|
output = dev::jsonPrettyPrint(m_compiler->metadata(_contract, _type));
|
||||||
|
|
||||||
if (m_args.count("output-dir"))
|
if (m_args.count("output-dir"))
|
||||||
createFile(_contract + suffix, m_compiler->metadata(_contract, _type));
|
createFile(_contract + suffix, output);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << title << endl;
|
cout << title << endl;
|
||||||
cout << m_compiler->metadata(_contract, _type) << endl;
|
cout << output << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -604,6 +611,12 @@ bool CommandLineInterface::processInput()
|
|||||||
<< boost::diagnostic_information(_exception);
|
<< boost::diagnostic_information(_exception);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
catch (UnimplementedFeatureError const& _exception)
|
||||||
|
{
|
||||||
|
cerr << "Unimplemented feature:" << endl
|
||||||
|
<< boost::diagnostic_information(_exception);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
catch (Error const& _error)
|
catch (Error const& _error)
|
||||||
{
|
{
|
||||||
if (_error.type() == Error::Type::DocstringParsingError)
|
if (_error.type() == Error::Type::DocstringParsingError)
|
||||||
@ -645,7 +658,7 @@ void CommandLineInterface::handleCombinedJSON()
|
|||||||
{
|
{
|
||||||
Json::Value contractData(Json::objectValue);
|
Json::Value contractData(Json::objectValue);
|
||||||
if (requests.count("abi"))
|
if (requests.count("abi"))
|
||||||
contractData["abi"] = m_compiler->interface(contractName);
|
contractData["abi"] = dev::jsonCompactPrint(m_compiler->interface(contractName));
|
||||||
if (requests.count("bin"))
|
if (requests.count("bin"))
|
||||||
contractData["bin"] = m_compiler->object(contractName).toHex();
|
contractData["bin"] = m_compiler->object(contractName).toHex();
|
||||||
if (requests.count("bin-runtime"))
|
if (requests.count("bin-runtime"))
|
||||||
@ -670,9 +683,9 @@ void CommandLineInterface::handleCombinedJSON()
|
|||||||
contractData["srcmap-runtime"] = map ? *map : "";
|
contractData["srcmap-runtime"] = map ? *map : "";
|
||||||
}
|
}
|
||||||
if (requests.count("devdoc"))
|
if (requests.count("devdoc"))
|
||||||
contractData["devdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecDev);
|
contractData["devdoc"] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecDev));
|
||||||
if (requests.count("userdoc"))
|
if (requests.count("userdoc"))
|
||||||
contractData["userdoc"] = m_compiler->metadata(contractName, DocumentationType::NatspecUser);
|
contractData["userdoc"] = dev::jsonCompactPrint(m_compiler->metadata(contractName, DocumentationType::NatspecUser));
|
||||||
output["contracts"][contractName] = contractData;
|
output["contracts"][contractName] = contractData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,7 +709,7 @@ void CommandLineInterface::handleCombinedJSON()
|
|||||||
output["sources"][sourceCode.first]["AST"] = converter.json();
|
output["sources"][sourceCode.first]["AST"] = converter.json();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cout << Json::FastWriter().write(output) << endl;
|
cout << dev::jsonCompactPrint(output) << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandLineInterface::handleAst(string const& _argStr)
|
void CommandLineInterface::handleAst(string const& _argStr)
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <libdevcore/Common.h>
|
#include <libdevcore/Common.h>
|
||||||
#include <libdevcore/CommonData.h>
|
#include <libdevcore/CommonData.h>
|
||||||
#include <libdevcore/CommonIO.h>
|
#include <libdevcore/CommonIO.h>
|
||||||
|
#include <libdevcore/JSON.h>
|
||||||
#include <libevmasm/Instruction.h>
|
#include <libevmasm/Instruction.h>
|
||||||
#include <libevmasm/GasMeter.h>
|
#include <libevmasm/GasMeter.h>
|
||||||
#include <libsolidity/parsing/Scanner.h>
|
#include <libsolidity/parsing/Scanner.h>
|
||||||
@ -189,6 +190,10 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
{
|
{
|
||||||
errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName));
|
errors.append(formatError(exception, "Internal compiler error", scannerFromSourceName));
|
||||||
}
|
}
|
||||||
|
catch (UnimplementedFeatureError const& exception)
|
||||||
|
{
|
||||||
|
errors.append(formatError(exception, "Unimplemented feature", scannerFromSourceName));
|
||||||
|
}
|
||||||
catch (Exception const& exception)
|
catch (Exception const& exception)
|
||||||
{
|
{
|
||||||
errors.append("Exception during compilation: " + boost::diagnostic_information(exception));
|
errors.append("Exception during compilation: " + boost::diagnostic_information(exception));
|
||||||
@ -209,7 +214,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
for (string const& contractName: compiler.contractNames())
|
for (string const& contractName: compiler.contractNames())
|
||||||
{
|
{
|
||||||
Json::Value contractData(Json::objectValue);
|
Json::Value contractData(Json::objectValue);
|
||||||
contractData["interface"] = compiler.interface(contractName);
|
contractData["interface"] = dev::jsonCompactPrint(compiler.interface(contractName));
|
||||||
contractData["bytecode"] = compiler.object(contractName).toHex();
|
contractData["bytecode"] = compiler.object(contractName).toHex();
|
||||||
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
|
contractData["runtimeBytecode"] = compiler.runtimeObject(contractName).toHex();
|
||||||
contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
|
contractData["opcodes"] = solidity::disassemble(compiler.object(contractName).bytecode);
|
||||||
@ -270,7 +275,7 @@ string compile(StringMap const& _sources, bool _optimize, CStyleReadFileCallback
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Json::FastWriter().write(output);
|
return dev::jsonCompactPrint(output);
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
@ -288,7 +293,7 @@ string compileMulti(string const& _input, bool _optimize, CStyleReadFileCallback
|
|||||||
errors.append("Error parsing input JSON: " + reader.getFormattedErrorMessages());
|
errors.append("Error parsing input JSON: " + reader.getFormattedErrorMessages());
|
||||||
Json::Value output(Json::objectValue);
|
Json::Value output(Json::objectValue);
|
||||||
output["errors"] = errors;
|
output["errors"] = errors;
|
||||||
return Json::FastWriter().write(output);
|
return dev::jsonCompactPrint(output);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
import "./Token.sol";
|
import "./Token.sol";
|
||||||
|
|
||||||
contract StandardToken is Token {
|
contract StandardToken is Token {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
contract Token {
|
contract Token {
|
||||||
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
event Transfer(address indexed _from, address indexed _to, uint256 _value);
|
||||||
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
import "./owned.sol";
|
import "./owned.sol";
|
||||||
|
|
||||||
contract mortal is owned {
|
contract mortal is owned {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
contract owned {
|
contract owned {
|
||||||
address owner;
|
address owner;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
pragma solidity ^0.4.0;
|
||||||
|
|
||||||
import "./owned.sol";
|
import "./owned.sol";
|
||||||
import "./mortal.sol";
|
import "./mortal.sol";
|
||||||
import "./Token.sol";
|
import "./Token.sol";
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
cmake_policy(SET CMP0015 NEW)
|
cmake_policy(SET CMP0015 NEW)
|
||||||
|
|
||||||
aux_source_directory(. SRC_LIST)
|
aux_source_directory(. SRC_LIST)
|
||||||
aux_source_directory(contracts SRC_LIST)
|
aux_source_directory(libdevcore SRC_LIST)
|
||||||
|
aux_source_directory(libevmasm SRC_LIST)
|
||||||
aux_source_directory(libsolidity SRC_LIST)
|
aux_source_directory(libsolidity SRC_LIST)
|
||||||
|
aux_source_directory(contracts SRC_LIST)
|
||||||
|
|
||||||
get_filename_component(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
|
get_filename_component(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE)
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ contract Wallet is multisig, multiowned, daylimit {
|
|||||||
|
|
||||||
// constructor - just pass on the owner array to the multiowned and
|
// constructor - just pass on the owner array to the multiowned and
|
||||||
// the limit to daylimit
|
// the limit to daylimit
|
||||||
function Wallet(address[] _owners, uint _required, uint _daylimit)
|
function Wallet(address[] _owners, uint _required, uint _daylimit) payable
|
||||||
multiowned(_owners, _required) daylimit(_daylimit) {
|
multiowned(_owners, _required) daylimit(_daylimit) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
57
test/libdevcore/SwarmHash.cpp
Normal file
57
test/libdevcore/SwarmHash.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Unit tests for the swarm hash computation routine.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libdevcore/SwarmHash.h>
|
||||||
|
|
||||||
|
#include "../TestHelper.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(SwarmHash)
|
||||||
|
|
||||||
|
string swarmHashHex(bytes const& _input)
|
||||||
|
{
|
||||||
|
return toHex(swarmHash(_input).asBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_zeros)
|
||||||
|
{
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes()), string("011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x1000 - 1, 0)), string("32f0faabc4265ac238cd945087133ce3d7e9bb2e536053a812b5373c54043adb"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x1000, 0)), string("411dd45de7246e94589ff5888362c41e85bd3e582a92d0fda8f0e90b76439bec"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x1000 + 1, 0)), string("69754a0098432bbc2e84fe1205276870748a61a065ab6ef44d6a2e7b13ce044d"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x2000 - 1, 0)), string("69ad3c581043404f775ffa8d6f1b25ad4a9ee812971190e90209c0966116a321"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x2000, 0)), string("f00222373ff82d0a178dc6271c78953e9c88f74130a52d401f5ec51475f63c43"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x2000 + 1, 0)), string("86d6773e79e02fd8145ee1aedba89ace0c15f2566db1249654000039a9a134bf"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x80000, 0)), string("cc0854fe2c6b98e920d5c14b1a88e6d4223e55b8f78883f60939aa2485e361bf"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x80020, 0)), string("ee9ffca246e70d3704740ba4df450fa6988d14a1c2439c7e734c7a77a4eb6fd3"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(0x800020, 0)), string("78b90b20c90559fb904535181a7c28929ea2f30a2329dbc25232de579709f12f"));
|
||||||
|
BOOST_CHECK_EQUAL(swarmHashHex(bytes(2095104, 0)), string("a9958184589fc11b4027a4c233e777ebe2e99c66f96b74aef2a0638a94dd5439"));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
50
test/libevmasm/SourceLocation.cpp
Normal file
50
test/libevmasm/SourceLocation.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
This file is part of cpp-ethereum.
|
||||||
|
|
||||||
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
cpp-ethereum is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @author Yoichi Hirai <yoichi@ethereum.org>
|
||||||
|
* @date 2016
|
||||||
|
* Unit tests for the SourceLocation class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <libevmasm/SourceLocation.h>
|
||||||
|
|
||||||
|
#include "../TestHelper.h"
|
||||||
|
|
||||||
|
namespace dev
|
||||||
|
{
|
||||||
|
namespace solidity
|
||||||
|
{
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE(SourceLocationTest)
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(test_fail)
|
||||||
|
{
|
||||||
|
BOOST_CHECK(SourceLocation() == SourceLocation());
|
||||||
|
BOOST_CHECK(SourceLocation(0, 3, std::make_shared<std::string>("sourceA")) != SourceLocation(0, 3, std::make_shared<std::string>("sourceB")));
|
||||||
|
BOOST_CHECK(SourceLocation(0, 3, std::make_shared<std::string>("source")) == SourceLocation(0, 3, std::make_shared<std::string>("source")));
|
||||||
|
BOOST_CHECK(SourceLocation(3, 7, std::make_shared<std::string>("source")).contains(SourceLocation(4, 6, std::make_shared<std::string>("source"))));
|
||||||
|
BOOST_CHECK(!SourceLocation(3, 7, std::make_shared<std::string>("sourceA")).contains(SourceLocation(4, 6, std::make_shared<std::string>("sourceB"))));
|
||||||
|
BOOST_CHECK(SourceLocation(3, 7, std::make_shared<std::string>("sourceA")) < SourceLocation(4, 6, std::make_shared<std::string>("sourceB")));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // end namespaces
|
@ -94,20 +94,6 @@ BOOST_AUTO_TEST_CASE(using_for_directive)
|
|||||||
BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint");
|
BOOST_CHECK_EQUAL(usingFor["children"][1]["attributes"]["name"], "uint");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(enum_definition)
|
|
||||||
{
|
|
||||||
CompilerStack c;
|
|
||||||
c.addSource("a", "contract C { enum E {} }");
|
|
||||||
c.parse();
|
|
||||||
map<string, unsigned> sourceIndices;
|
|
||||||
sourceIndices["a"] = 1;
|
|
||||||
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
|
|
||||||
Json::Value enumDefinition = astJson["children"][0]["children"][0];
|
|
||||||
BOOST_CHECK_EQUAL(enumDefinition["name"], "EnumDefinition");
|
|
||||||
BOOST_CHECK_EQUAL(enumDefinition["attributes"]["name"], "E");
|
|
||||||
BOOST_CHECK_EQUAL(enumDefinition["src"], "13:9:1");
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(enum_value)
|
BOOST_AUTO_TEST_CASE(enum_value)
|
||||||
{
|
{
|
||||||
CompilerStack c;
|
CompilerStack c;
|
||||||
@ -211,6 +197,37 @@ BOOST_AUTO_TEST_CASE(non_utf8)
|
|||||||
BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos);
|
BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type)
|
||||||
|
{
|
||||||
|
CompilerStack c;
|
||||||
|
c.addSource("a",
|
||||||
|
"contract C { function f(function() external payable returns (uint) x) "
|
||||||
|
"returns (function() external constant returns (uint)) {} }"
|
||||||
|
);
|
||||||
|
c.parse();
|
||||||
|
map<string, unsigned> sourceIndices;
|
||||||
|
sourceIndices["a"] = 1;
|
||||||
|
Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json();
|
||||||
|
Json::Value fun = astJson["children"][0]["children"][0];
|
||||||
|
BOOST_CHECK_EQUAL(fun["name"], "FunctionDefinition");
|
||||||
|
Json::Value argument = fun["children"][0]["children"][0];
|
||||||
|
BOOST_CHECK_EQUAL(argument["name"], "VariableDeclaration");
|
||||||
|
BOOST_CHECK_EQUAL(argument["attributes"]["name"], "x");
|
||||||
|
BOOST_CHECK_EQUAL(argument["attributes"]["type"], "function () payable external returns (uint256)");
|
||||||
|
Json::Value funType = argument["children"][0];
|
||||||
|
BOOST_CHECK_EQUAL(funType["attributes"]["constant"], false);
|
||||||
|
BOOST_CHECK_EQUAL(funType["attributes"]["payable"], true);
|
||||||
|
BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
|
||||||
|
Json::Value retval = fun["children"][1]["children"][0];
|
||||||
|
BOOST_CHECK_EQUAL(retval["name"], "VariableDeclaration");
|
||||||
|
BOOST_CHECK_EQUAL(retval["attributes"]["name"], "");
|
||||||
|
BOOST_CHECK_EQUAL(retval["attributes"]["type"], "function () constant external returns (uint256)");
|
||||||
|
funType = retval["children"][0];
|
||||||
|
BOOST_CHECK_EQUAL(funType["attributes"]["constant"], true);
|
||||||
|
BOOST_CHECK_EQUAL(funType["attributes"]["payable"], false);
|
||||||
|
BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external");
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -91,8 +91,10 @@ void checkAssemblyLocations(AssemblyItems const& _items, vector<SourceLocation>
|
|||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
_items[i].location() == _locations[i],
|
_items[i].location() == _locations[i],
|
||||||
"Location mismatch for assembly item " + to_string(i) + ". Found: " +
|
"Location mismatch for assembly item " + to_string(i) + ". Found: " +
|
||||||
|
(_items[i].location().sourceName ? *_items[i].location().sourceName + ":" : "(null source name)") +
|
||||||
to_string(_items[i].location().start) + "-" +
|
to_string(_items[i].location().start) + "-" +
|
||||||
to_string(_items[i].location().end) + ", expected: " +
|
to_string(_items[i].location().end) + ", expected: " +
|
||||||
|
(_locations[i].sourceName ? *_locations[i].sourceName + ":" : "(null source name)") +
|
||||||
to_string(_locations[i].start) + "-" +
|
to_string(_locations[i].start) + "-" +
|
||||||
to_string(_locations[i].end));
|
to_string(_locations[i].end));
|
||||||
}
|
}
|
||||||
@ -111,13 +113,13 @@ BOOST_AUTO_TEST_CASE(location_test)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
shared_ptr<string const> n = make_shared<string>("source");
|
shared_ptr<string const> n = make_shared<string>("");
|
||||||
AssemblyItems items = compileContract(sourceCode);
|
AssemblyItems items = compileContract(sourceCode);
|
||||||
vector<SourceLocation> locations =
|
vector<SourceLocation> locations =
|
||||||
vector<SourceLocation>(18, SourceLocation(2, 75, n)) +
|
vector<SourceLocation>(16, SourceLocation(2, 75, n)) +
|
||||||
vector<SourceLocation>(31, SourceLocation(20, 72, n)) +
|
vector<SourceLocation>(27, SourceLocation(20, 72, n)) +
|
||||||
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
|
vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} +
|
||||||
vector<SourceLocation>(4, SourceLocation(58, 67, n)) +
|
vector<SourceLocation>(2, SourceLocation(58, 67, n)) +
|
||||||
vector<SourceLocation>(3, SourceLocation(20, 72, n));
|
vector<SourceLocation>(3, SourceLocation(20, 72, n));
|
||||||
checkAssemblyLocations(items, locations);
|
checkAssemblyLocations(items, locations);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ namespace test
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
bool successParse(std::string const& _source, bool _assemble = false)
|
bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true)
|
||||||
{
|
{
|
||||||
assembly::InlineAssemblyStack stack;
|
assembly::InlineAssemblyStack stack;
|
||||||
try
|
try
|
||||||
@ -51,8 +51,9 @@ bool successParse(std::string const& _source, bool _assemble = false)
|
|||||||
if (_assemble)
|
if (_assemble)
|
||||||
{
|
{
|
||||||
stack.assemble();
|
stack.assemble();
|
||||||
if (!stack.errors().empty() && !Error::containsOnlyWarnings(stack.errors()))
|
if (!stack.errors().empty())
|
||||||
return false;
|
if (!_allowWarnings || !Error::containsOnlyWarnings(stack.errors()))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (FatalError const&)
|
catch (FatalError const&)
|
||||||
@ -67,9 +68,9 @@ bool successParse(std::string const& _source, bool _assemble = false)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool successAssemble(string const& _source)
|
bool successAssemble(string const& _source, bool _allowWarnings = true)
|
||||||
{
|
{
|
||||||
return successParse(_source, true);
|
return successParse(_source, true, _allowWarnings);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -169,6 +170,18 @@ BOOST_AUTO_TEST_CASE(magic_variables)
|
|||||||
BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover }"));
|
BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(imbalanced_stack)
|
||||||
|
{
|
||||||
|
BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
|
||||||
|
BOOST_CHECK(!successAssemble("{ 1 }", false));
|
||||||
|
BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(error_tag)
|
||||||
|
{
|
||||||
|
BOOST_CHECK(successAssemble("{ invalidJumpLabel }"));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -40,14 +40,14 @@ public:
|
|||||||
void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
|
void checkInterface(std::string const& _code, std::string const& _expectedInterfaceString)
|
||||||
{
|
{
|
||||||
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing contract failed");
|
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing contract failed");
|
||||||
std::string generatedInterfaceString = m_compilerStack.metadata("", DocumentationType::ABIInterface);
|
|
||||||
Json::Value generatedInterface;
|
Json::Value generatedInterface = m_compilerStack.metadata("", DocumentationType::ABIInterface);
|
||||||
m_reader.parse(generatedInterfaceString, generatedInterface);
|
|
||||||
Json::Value expectedInterface;
|
Json::Value expectedInterface;
|
||||||
m_reader.parse(_expectedInterfaceString, expectedInterface);
|
m_reader.parse(_expectedInterfaceString, expectedInterface);
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
expectedInterface == generatedInterface,
|
expectedInterface == generatedInterface,
|
||||||
"Expected:\n" << expectedInterface.toStyledString() << "\n but got:\n" << generatedInterface.toStyledString()
|
"Expected:\n" << expectedInterface.toStyledString() <<
|
||||||
|
"\n but got:\n" << generatedInterface.toStyledString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,6 +524,7 @@ BOOST_AUTO_TEST_CASE(constructor_abi)
|
|||||||
"type": "bool"
|
"type": "bool"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"payable": false,
|
||||||
"type": "constructor"
|
"type": "constructor"
|
||||||
}
|
}
|
||||||
])";
|
])";
|
||||||
@ -567,6 +568,7 @@ BOOST_AUTO_TEST_CASE(return_param_in_abi)
|
|||||||
"type": "uint8"
|
"type": "uint8"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"payable": false,
|
||||||
"type": "constructor"
|
"type": "constructor"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -684,7 +686,7 @@ BOOST_AUTO_TEST_CASE(payable_function)
|
|||||||
checkInterface(sourceCode, interface);
|
checkInterface(sourceCode, interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(payable_fallback_unction)
|
BOOST_AUTO_TEST_CASE(payable_fallback_function)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
contract test {
|
contract test {
|
||||||
@ -703,6 +705,32 @@ BOOST_AUTO_TEST_CASE(payable_fallback_unction)
|
|||||||
checkInterface(sourceCode, interface);
|
checkInterface(sourceCode, interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract test {
|
||||||
|
function g(function(uint) external returns (uint) x) {}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
char const* interface = R"(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"constant" : false,
|
||||||
|
"payable": false,
|
||||||
|
"inputs": [{
|
||||||
|
"name": "x",
|
||||||
|
"type": "function"
|
||||||
|
}],
|
||||||
|
"name": "g",
|
||||||
|
"outputs": [],
|
||||||
|
"type" : "function"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)";
|
||||||
|
checkInterface(sourceCode, interface);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -353,6 +353,34 @@ BOOST_AUTO_TEST_CASE(while_loop)
|
|||||||
testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5);
|
testSolidityAgainstCppOnRange("f(uint256)", while_loop_cpp, 0, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(do_while_loop)
|
||||||
|
{
|
||||||
|
char const* sourceCode = "contract test {\n"
|
||||||
|
" function f(uint n) returns(uint nfac) {\n"
|
||||||
|
" nfac = 1;\n"
|
||||||
|
" var i = 2;\n"
|
||||||
|
" do { nfac *= i++; } while (i <= n);\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
|
||||||
|
auto do_while_loop_cpp = [](u256 const& n) -> u256
|
||||||
|
{
|
||||||
|
u256 nfac = 1;
|
||||||
|
u256 i = 2;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nfac *= i++;
|
||||||
|
}
|
||||||
|
while (i <= n);
|
||||||
|
|
||||||
|
return nfac;
|
||||||
|
};
|
||||||
|
|
||||||
|
testSolidityAgainstCppOnRange("f(uint256)", do_while_loop_cpp, 0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(nested_loops)
|
BOOST_AUTO_TEST_CASE(nested_loops)
|
||||||
{
|
{
|
||||||
// tests that break and continue statements in nested loops jump to the correct place
|
// tests that break and continue statements in nested loops jump to the correct place
|
||||||
@ -1309,6 +1337,7 @@ BOOST_AUTO_TEST_CASE(struct_accessor)
|
|||||||
BOOST_AUTO_TEST_CASE(balance)
|
BOOST_AUTO_TEST_CASE(balance)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
char const* sourceCode = "contract test {\n"
|
||||||
|
" function test() payable {}\n"
|
||||||
" function getBalance() returns (uint256 balance) {\n"
|
" function getBalance() returns (uint256 balance) {\n"
|
||||||
" return address(this).balance;\n"
|
" return address(this).balance;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
@ -1320,6 +1349,7 @@ BOOST_AUTO_TEST_CASE(balance)
|
|||||||
BOOST_AUTO_TEST_CASE(blockchain)
|
BOOST_AUTO_TEST_CASE(blockchain)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
char const* sourceCode = "contract test {\n"
|
||||||
|
" function test() payable {}\n"
|
||||||
" function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
|
" function someInfo() payable returns (uint256 value, address coinbase, uint256 blockNumber) {\n"
|
||||||
" value = msg.value;\n"
|
" value = msg.value;\n"
|
||||||
" coinbase = block.coinbase;\n"
|
" coinbase = block.coinbase;\n"
|
||||||
@ -1535,6 +1565,7 @@ BOOST_AUTO_TEST_CASE(convert_uint_to_fixed_bytes_greater_size)
|
|||||||
BOOST_AUTO_TEST_CASE(send_ether)
|
BOOST_AUTO_TEST_CASE(send_ether)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
char const* sourceCode = "contract test {\n"
|
||||||
|
" function test() payable {}\n"
|
||||||
" function a(address addr, uint amount) returns (uint ret) {\n"
|
" function a(address addr, uint amount) returns (uint ret) {\n"
|
||||||
" addr.send(amount);\n"
|
" addr.send(amount);\n"
|
||||||
" return address(this).balance;\n"
|
" return address(this).balance;\n"
|
||||||
@ -1647,6 +1678,7 @@ BOOST_AUTO_TEST_CASE(log_in_constructor)
|
|||||||
BOOST_AUTO_TEST_CASE(suicide)
|
BOOST_AUTO_TEST_CASE(suicide)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
char const* sourceCode = "contract test {\n"
|
||||||
|
" function test() payable {}\n"
|
||||||
" function a(address receiver) returns (uint ret) {\n"
|
" function a(address receiver) returns (uint ret) {\n"
|
||||||
" suicide(receiver);\n"
|
" suicide(receiver);\n"
|
||||||
" return 10;\n"
|
" return 10;\n"
|
||||||
@ -1663,6 +1695,7 @@ BOOST_AUTO_TEST_CASE(suicide)
|
|||||||
BOOST_AUTO_TEST_CASE(selfdestruct)
|
BOOST_AUTO_TEST_CASE(selfdestruct)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
char const* sourceCode = "contract test {\n"
|
||||||
|
" function test() payable {}\n"
|
||||||
" function a(address receiver) returns (uint ret) {\n"
|
" function a(address receiver) returns (uint ret) {\n"
|
||||||
" selfdestruct(receiver);\n"
|
" selfdestruct(receiver);\n"
|
||||||
" return 10;\n"
|
" return 10;\n"
|
||||||
@ -2928,24 +2961,24 @@ BOOST_AUTO_TEST_CASE(generic_call)
|
|||||||
BOOST_AUTO_TEST_CASE(generic_callcode)
|
BOOST_AUTO_TEST_CASE(generic_callcode)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"**(
|
char const* sourceCode = R"**(
|
||||||
contract receiver {
|
contract Receiver {
|
||||||
uint public received;
|
uint public received;
|
||||||
function receive(uint256 x) payable { received = x; }
|
function receive(uint256 x) payable { received = x; }
|
||||||
}
|
}
|
||||||
contract sender {
|
contract Sender {
|
||||||
uint public received;
|
uint public received;
|
||||||
function sender() payable { }
|
function Sender() payable { }
|
||||||
function doSend(address rec) returns (uint d)
|
function doSend(address rec) returns (uint d)
|
||||||
{
|
{
|
||||||
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
|
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
|
||||||
rec.callcode.value(2)(signature, 23);
|
rec.callcode.value(2)(signature, 23);
|
||||||
return receiver(rec).received();
|
return Receiver(rec).received();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)**";
|
)**";
|
||||||
compileAndRun(sourceCode, 0, "receiver");
|
compileAndRun(sourceCode, 0, "Receiver");
|
||||||
u160 const c_receiverAddress = m_contractAddress;
|
u160 const c_receiverAddress = m_contractAddress;
|
||||||
compileAndRun(sourceCode, 50, "sender");
|
compileAndRun(sourceCode, 50, "Sender");
|
||||||
u160 const c_senderAddress = m_contractAddress;
|
u160 const c_senderAddress = m_contractAddress;
|
||||||
BOOST_CHECK(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(0));
|
BOOST_CHECK(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(0));
|
||||||
BOOST_CHECK(callContractFunction("received()") == encodeArgs(23));
|
BOOST_CHECK(callContractFunction("received()") == encodeArgs(23));
|
||||||
@ -2960,16 +2993,18 @@ BOOST_AUTO_TEST_CASE(generic_callcode)
|
|||||||
BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"**(
|
char const* sourceCode = R"**(
|
||||||
contract receiver {
|
contract Receiver {
|
||||||
uint public received;
|
uint public received;
|
||||||
address public sender;
|
address public sender;
|
||||||
uint public value;
|
uint public value;
|
||||||
|
function Receiver() payable {}
|
||||||
function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; }
|
function receive(uint256 x) payable { received = x; sender = msg.sender; value = msg.value; }
|
||||||
}
|
}
|
||||||
contract sender {
|
contract Sender {
|
||||||
uint public received;
|
uint public received;
|
||||||
address public sender;
|
address public sender;
|
||||||
uint public value;
|
uint public value;
|
||||||
|
function Sender() payable {}
|
||||||
function doSend(address rec) payable
|
function doSend(address rec) payable
|
||||||
{
|
{
|
||||||
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
|
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
|
||||||
@ -2977,9 +3012,9 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)**";
|
)**";
|
||||||
compileAndRun(sourceCode, 0, "receiver");
|
compileAndRun(sourceCode, 0, "Receiver");
|
||||||
u160 const c_receiverAddress = m_contractAddress;
|
u160 const c_receiverAddress = m_contractAddress;
|
||||||
compileAndRun(sourceCode, 50, "sender");
|
compileAndRun(sourceCode, 50, "Sender");
|
||||||
u160 const c_senderAddress = m_contractAddress;
|
u160 const c_senderAddress = m_contractAddress;
|
||||||
BOOST_CHECK(m_sender != c_senderAddress); // just for sanity
|
BOOST_CHECK(m_sender != c_senderAddress); // just for sanity
|
||||||
BOOST_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress) == encodeArgs());
|
BOOST_CHECK(callContractFunctionWithValue("doSend(address)", 11, c_receiverAddress) == encodeArgs());
|
||||||
@ -3314,6 +3349,42 @@ BOOST_AUTO_TEST_CASE(using_enums)
|
|||||||
BOOST_CHECK(callContractFunction("getChoice()") == encodeArgs(2));
|
BOOST_CHECK(callContractFunction("getChoice()") == encodeArgs(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(enum_explicit_overflow)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract test {
|
||||||
|
enum ActionChoices { GoLeft, GoRight, GoStraight }
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
function getChoiceExp(uint x) returns (uint d)
|
||||||
|
{
|
||||||
|
choice = ActionChoices(x);
|
||||||
|
d = uint256(choice);
|
||||||
|
}
|
||||||
|
function getChoiceFromSigned(int x) returns (uint d)
|
||||||
|
{
|
||||||
|
choice = ActionChoices(x);
|
||||||
|
d = uint256(choice);
|
||||||
|
}
|
||||||
|
function getChoiceFromNegativeLiteral() returns (uint d)
|
||||||
|
{
|
||||||
|
choice = ActionChoices(-1);
|
||||||
|
d = uint256(choice);
|
||||||
|
}
|
||||||
|
ActionChoices choice;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
// These should throw
|
||||||
|
BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 3) == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("getChoiceFromSigned(int256)", -1) == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("getChoiceFromNegativeLiteral()") == encodeArgs());
|
||||||
|
// These should work
|
||||||
|
BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 2) == encodeArgs(2));
|
||||||
|
BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 0) == encodeArgs(0));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name)
|
BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -4435,6 +4506,67 @@ BOOST_AUTO_TEST_CASE(external_types_in_calls)
|
|||||||
BOOST_CHECK(callContractFunction("t2()") == encodeArgs(u256(9)));
|
BOOST_CHECK(callContractFunction("t2()") == encodeArgs(u256(9)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(invalid_enum_as_external_ret)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
enum X { A, B }
|
||||||
|
|
||||||
|
function test_return() returns (X) {
|
||||||
|
X garbled;
|
||||||
|
assembly {
|
||||||
|
garbled := 5
|
||||||
|
}
|
||||||
|
return garbled;
|
||||||
|
}
|
||||||
|
function test_inline_assignment() returns (X _ret) {
|
||||||
|
assembly {
|
||||||
|
_ret := 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function test_assignment() returns (X _ret) {
|
||||||
|
X tmp;
|
||||||
|
assembly {
|
||||||
|
tmp := 5
|
||||||
|
}
|
||||||
|
_ret = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
// both should throw
|
||||||
|
BOOST_CHECK(callContractFunction("test_return()") == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("test_inline_assignment()") == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("test_assignment()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(invalid_enum_as_external_arg)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
enum X { A, B }
|
||||||
|
|
||||||
|
function tested (X x) returns (uint) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() returns (uint) {
|
||||||
|
X garbled;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
garbled := 5
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.tested(garbled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
// should throw
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes)
|
BOOST_AUTO_TEST_CASE(proper_order_of_overwriting_of_attributes)
|
||||||
{
|
{
|
||||||
// bug #1798
|
// bug #1798
|
||||||
@ -4693,6 +4825,7 @@ BOOST_AUTO_TEST_CASE(failing_send)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
contract Main {
|
contract Main {
|
||||||
|
function Main() payable {}
|
||||||
function callHelper(address _a) returns (bool r, uint bal) {
|
function callHelper(address _a) returns (bool r, uint bal) {
|
||||||
r = !_a.send(5);
|
r = !_a.send(5);
|
||||||
bal = this.balance;
|
bal = this.balance;
|
||||||
@ -4715,6 +4848,7 @@ BOOST_AUTO_TEST_CASE(send_zero_ether)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
contract Main {
|
contract Main {
|
||||||
|
function Main() payable {}
|
||||||
function s() returns (bool) {
|
function s() returns (bool) {
|
||||||
var r = new Receiver();
|
var r = new Receiver();
|
||||||
return r.send(0);
|
return r.send(0);
|
||||||
@ -6216,6 +6350,7 @@ BOOST_AUTO_TEST_CASE(reject_ether_sent_to_library)
|
|||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
library lib {}
|
library lib {}
|
||||||
contract c {
|
contract c {
|
||||||
|
function c() payable {}
|
||||||
function f(address x) returns (bool) {
|
function f(address x) returns (bool) {
|
||||||
return x.send(1);
|
return x.send(1);
|
||||||
}
|
}
|
||||||
@ -6325,6 +6460,20 @@ BOOST_AUTO_TEST_CASE(decayed_tuple)
|
|||||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inline_tuple_with_rational_numbers)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract c {
|
||||||
|
function f() returns (int8) {
|
||||||
|
int8[5] memory foo3 = [int8(1), -1, 0, 0, 0];
|
||||||
|
return foo3[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode);
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1)));
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(destructuring_assignment)
|
BOOST_AUTO_TEST_CASE(destructuring_assignment)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -7140,6 +7289,7 @@ BOOST_AUTO_TEST_CASE(failed_create)
|
|||||||
contract D { function D() payable {} }
|
contract D { function D() payable {} }
|
||||||
contract C {
|
contract C {
|
||||||
uint public x;
|
uint public x;
|
||||||
|
function C() payable {}
|
||||||
function f(uint amount) returns (address) {
|
function f(uint amount) returns (address) {
|
||||||
x++;
|
x++;
|
||||||
return (new D).value(amount)();
|
return (new D).value(amount)();
|
||||||
@ -7253,7 +7403,7 @@ BOOST_AUTO_TEST_CASE(mutex)
|
|||||||
}
|
}
|
||||||
contract Fund is mutexed {
|
contract Fund is mutexed {
|
||||||
uint shares;
|
uint shares;
|
||||||
function Fund() { shares = msg.value; }
|
function Fund() payable { shares = msg.value; }
|
||||||
function withdraw(uint amount) protected returns (uint) {
|
function withdraw(uint amount) protected returns (uint) {
|
||||||
// NOTE: It is very bad practice to write this function this way.
|
// NOTE: It is very bad practice to write this function this way.
|
||||||
// Please refer to the documentation of how to do this properly.
|
// Please refer to the documentation of how to do this properly.
|
||||||
@ -7467,6 +7617,568 @@ BOOST_AUTO_TEST_CASE(mem_resize_is_not_paid_at_call)
|
|||||||
BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7)));
|
BOOST_CHECK(callContractFunction("f(address)", cAddrOpt) == encodeArgs(u256(7)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(calling_uninitialized_function)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function intern() returns (uint) {
|
||||||
|
function (uint) internal returns (uint) x;
|
||||||
|
x(2);
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
function extern() returns (uint) {
|
||||||
|
function (uint) external returns (uint) x;
|
||||||
|
x(2);
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
// This should throw exceptions
|
||||||
|
BOOST_CHECK(callContractFunction("intern()") == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("extern()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function() internal returns (uint) x;
|
||||||
|
int mutex;
|
||||||
|
function t() returns (uint) {
|
||||||
|
if (mutex > 0)
|
||||||
|
return 7;
|
||||||
|
mutex = 1;
|
||||||
|
// Avoid re-executing this function if we jump somewhere.
|
||||||
|
x();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("t()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(pass_function_types_internally)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(uint x) returns (uint) {
|
||||||
|
return eval(g, x);
|
||||||
|
}
|
||||||
|
function eval(function(uint) returns (uint) x, uint a) internal returns (uint) {
|
||||||
|
return x(a);
|
||||||
|
}
|
||||||
|
function g(uint x) returns (uint) { return x + 1; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256)", 7) == encodeArgs(u256(8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(pass_function_types_externally)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f(uint x) returns (uint) {
|
||||||
|
return this.eval(this.g, x);
|
||||||
|
}
|
||||||
|
function f2(uint x) returns (uint) {
|
||||||
|
return eval(this.g, x);
|
||||||
|
}
|
||||||
|
function eval(function(uint) external returns (uint) x, uint a) returns (uint) {
|
||||||
|
return x(a);
|
||||||
|
}
|
||||||
|
function g(uint x) returns (uint) { return x + 1; }
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256)", 7) == encodeArgs(u256(8)));
|
||||||
|
BOOST_CHECK(callContractFunction("f2(uint256)", 7) == encodeArgs(u256(8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(receive_external_function_type)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function g() returns (uint) { return 7; }
|
||||||
|
function f(function() external returns (uint) g) returns (uint) {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction(
|
||||||
|
"f(function)",
|
||||||
|
m_contractAddress.asBytes() + FixedHash<4>(dev::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0)
|
||||||
|
) == encodeArgs(u256(7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(return_external_function_type)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function g() {}
|
||||||
|
function f() returns (function() external) {
|
||||||
|
return this.g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(
|
||||||
|
callContractFunction("f()") ==
|
||||||
|
m_contractAddress.asBytes() + FixedHash<4>(dev::keccak256("g()")).asBytes() + bytes(32 - 4 - 20, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(store_function)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract Other {
|
||||||
|
function addTwo(uint x) returns (uint) { return x + 2; }
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function (function (uint) external returns (uint)) returns (uint) ev;
|
||||||
|
function (uint) external returns (uint) x;
|
||||||
|
function store(function(uint) external returns (uint) y) {
|
||||||
|
x = y;
|
||||||
|
}
|
||||||
|
function eval(function(uint) external returns (uint) y) returns (uint) {
|
||||||
|
return y(7);
|
||||||
|
}
|
||||||
|
function t() returns (uint) {
|
||||||
|
ev = eval;
|
||||||
|
this.store((new Other()).addTwo);
|
||||||
|
return ev(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("t()") == encodeArgs(u256(9)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(store_function_in_constructor)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
uint public result_in_constructor;
|
||||||
|
function (uint) internal returns (uint) x;
|
||||||
|
function C () {
|
||||||
|
x = double;
|
||||||
|
result_in_constructor = use(2);
|
||||||
|
}
|
||||||
|
function double(uint _arg) returns (uint _ret) {
|
||||||
|
_ret = _arg * 2;
|
||||||
|
}
|
||||||
|
function use(uint _arg) returns (uint) {
|
||||||
|
return x(_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("use(uint256)", encodeArgs(u256(3))) == encodeArgs(u256(6)));
|
||||||
|
BOOST_CHECK(callContractFunction("result_in_constructor()") == encodeArgs(u256(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: store bound internal library functions
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(store_internal_unused_function_in_constructor)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function () internal returns (uint) x;
|
||||||
|
function C () {
|
||||||
|
x = unused;
|
||||||
|
}
|
||||||
|
function unused() internal returns (uint) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
function t() returns (uint) {
|
||||||
|
return x();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("t()") == encodeArgs(u256(7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(store_internal_unused_library_function_in_constructor)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
library L { function x() internal returns (uint) { return 7; } }
|
||||||
|
contract C {
|
||||||
|
function () internal returns (uint) x;
|
||||||
|
function C () {
|
||||||
|
x = L.x;
|
||||||
|
}
|
||||||
|
function t() returns (uint) {
|
||||||
|
return x();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("t()") == encodeArgs(u256(7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
uint public initial;
|
||||||
|
function C() {
|
||||||
|
initial = double(2);
|
||||||
|
}
|
||||||
|
function double(uint _arg) returns (uint _ret) {
|
||||||
|
_ret = _arg * 2;
|
||||||
|
}
|
||||||
|
function runtime(uint _arg) returns (uint) {
|
||||||
|
return double(_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("runtime(uint256)", encodeArgs(u256(3))) == encodeArgs(u256(6)));
|
||||||
|
BOOST_CHECK(callContractFunction("initial()") == encodeArgs(u256(4)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(same_function_in_construction_and_runtime_equality_check)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function (uint) internal returns (uint) x;
|
||||||
|
function C() {
|
||||||
|
x = double;
|
||||||
|
}
|
||||||
|
function test() returns (bool) {
|
||||||
|
return x == double;
|
||||||
|
}
|
||||||
|
function double(uint _arg) returns (uint _ret) {
|
||||||
|
_ret = _arg * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type_library_internal)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
library Utils {
|
||||||
|
function reduce(uint[] memory array, function(uint, uint) returns (uint) f, uint init) internal returns (uint) {
|
||||||
|
for (uint i = 0; i < array.length; i++) {
|
||||||
|
init = f(array[i], init);
|
||||||
|
}
|
||||||
|
return init;
|
||||||
|
}
|
||||||
|
function sum(uint a, uint b) internal returns (uint) {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function f(uint[] x) returns (uint) {
|
||||||
|
return Utils.reduce(x, Utils.sum, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("f(uint256[])", 0x20, 3, u256(1), u256(7), u256(3)) == encodeArgs(u256(11)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(call_function_returning_function)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract test {
|
||||||
|
function f0() returns (uint) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
function f1() internal returns (function() returns (uint)) {
|
||||||
|
return f0;
|
||||||
|
}
|
||||||
|
function f2() internal returns (function() returns (function () returns (uint))) {
|
||||||
|
return f1;
|
||||||
|
}
|
||||||
|
function f3() internal returns (function() returns (function () returns (function () returns (uint))))
|
||||||
|
{
|
||||||
|
return f2;
|
||||||
|
}
|
||||||
|
function f() returns (uint) {
|
||||||
|
function() returns(function() returns(function() returns(function() returns(uint)))) x;
|
||||||
|
x = f3;
|
||||||
|
return x()()()();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "test");
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(mapping_of_functions)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract Flow {
|
||||||
|
bool public success;
|
||||||
|
|
||||||
|
mapping (address => function () internal) stages;
|
||||||
|
|
||||||
|
function stage0() internal {
|
||||||
|
stages[msg.sender] = stage1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stage1() internal {
|
||||||
|
stages[msg.sender] = stage2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stage2() internal {
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Flow() {
|
||||||
|
stages[msg.sender] = stage0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() returns (uint) {
|
||||||
|
stages[msg.sender]();
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "Flow");
|
||||||
|
BOOST_CHECK(callContractFunction("success()") == encodeArgs(false));
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("success()") == encodeArgs(false));
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("success()") == encodeArgs(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(packed_functions)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
// these should take the same slot
|
||||||
|
function() returns (uint) a;
|
||||||
|
function() external returns (uint) b;
|
||||||
|
function() external returns (uint) c;
|
||||||
|
function() returns (uint) d;
|
||||||
|
uint8 public x;
|
||||||
|
|
||||||
|
function set() {
|
||||||
|
x = 2;
|
||||||
|
d = g;
|
||||||
|
c = this.h;
|
||||||
|
b = this.h;
|
||||||
|
a = g;
|
||||||
|
}
|
||||||
|
function t1() returns (uint) {
|
||||||
|
return a();
|
||||||
|
}
|
||||||
|
function t2() returns (uint) {
|
||||||
|
return b();
|
||||||
|
}
|
||||||
|
function t3() returns (uint) {
|
||||||
|
return a();
|
||||||
|
}
|
||||||
|
function t4() returns (uint) {
|
||||||
|
return b();
|
||||||
|
}
|
||||||
|
function g() returns (uint) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
function h() returns (uint) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("set()") == encodeArgs());
|
||||||
|
BOOST_CHECK(callContractFunction("t1()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("t2()") == encodeArgs(u256(8)));
|
||||||
|
BOOST_CHECK(callContractFunction("t3()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("t4()") == encodeArgs(u256(8)));
|
||||||
|
BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_memory_array)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function a(uint x) returns (uint) { return x + 1; }
|
||||||
|
function b(uint x) returns (uint) { return x + 2; }
|
||||||
|
function c(uint x) returns (uint) { return x + 3; }
|
||||||
|
function d(uint x) returns (uint) { return x + 5; }
|
||||||
|
function e(uint x) returns (uint) { return x + 8; }
|
||||||
|
function test(uint x, uint i) returns (uint) {
|
||||||
|
function(uint) internal returns (uint)[] memory arr =
|
||||||
|
new function(uint) internal returns (uint)[](10);
|
||||||
|
arr[0] = a;
|
||||||
|
arr[1] = b;
|
||||||
|
arr[2] = c;
|
||||||
|
arr[3] = d;
|
||||||
|
arr[4] = e;
|
||||||
|
return arr[i](x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(0)) == encodeArgs(u256(11)));
|
||||||
|
BOOST_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(1)) == encodeArgs(u256(12)));
|
||||||
|
BOOST_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(2)) == encodeArgs(u256(13)));
|
||||||
|
BOOST_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(3)) == encodeArgs(u256(15)));
|
||||||
|
BOOST_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(4)) == encodeArgs(u256(18)));
|
||||||
|
BOOST_CHECK(callContractFunction("test(uint256,uint256)", u256(10), u256(5)) == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_delete_storage)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function a() returns (uint) { return 7; }
|
||||||
|
function() internal returns (uint) y;
|
||||||
|
function set() returns (uint) {
|
||||||
|
y = a;
|
||||||
|
return y();
|
||||||
|
}
|
||||||
|
function d() returns (uint) {
|
||||||
|
delete y;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
function ca() returns (uint) {
|
||||||
|
return y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("set()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("ca()") == encodeArgs(u256(7)));
|
||||||
|
BOOST_CHECK(callContractFunction("d()") == encodeArgs(u256(1)));
|
||||||
|
BOOST_CHECK(callContractFunction("ca()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_delete_stack)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function a() returns (uint) { return 7; }
|
||||||
|
function test() returns (uint) {
|
||||||
|
var y = a;
|
||||||
|
delete y;
|
||||||
|
y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(copy_function_storage_array)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function() internal returns (uint)[] x;
|
||||||
|
function() internal returns (uint)[] y;
|
||||||
|
function test() returns (uint) {
|
||||||
|
x.length = 10;
|
||||||
|
x[9] = a;
|
||||||
|
y = x;
|
||||||
|
return y[9]();
|
||||||
|
}
|
||||||
|
function a() returns (uint) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_array_cross_calls)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract D {
|
||||||
|
function f(function() external returns (function() external returns (uint))[] x)
|
||||||
|
returns (function() external returns (uint)[3] r)
|
||||||
|
{
|
||||||
|
r[0] = x[0]();
|
||||||
|
r[1] = x[1]();
|
||||||
|
r[2] = x[2]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract C {
|
||||||
|
function test() returns (uint, uint, uint) {
|
||||||
|
function() external returns (function() external returns (uint))[] memory x =
|
||||||
|
new function() external returns (function() external returns (uint))[](10);
|
||||||
|
for (uint i = 0; i < x.length; i ++)
|
||||||
|
x[i] = this.h;
|
||||||
|
x[0] = this.htwo;
|
||||||
|
var y = (new D()).f(x);
|
||||||
|
return (y[0](), y[1](), y[2]());
|
||||||
|
}
|
||||||
|
function e() returns (uint) { return 5; }
|
||||||
|
function f() returns (uint) { return 6; }
|
||||||
|
function g() returns (uint) { return 7; }
|
||||||
|
uint counter;
|
||||||
|
function h() returns (function() external returns (uint)) {
|
||||||
|
return counter++ == 0 ? this.f : this.g;
|
||||||
|
}
|
||||||
|
function htwo() returns (function() external returns (uint)) {
|
||||||
|
return this.e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(5), u256(6), u256(7)));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function() internal returns (uint)[20] x;
|
||||||
|
int mutex;
|
||||||
|
function one() returns (uint) {
|
||||||
|
function() internal returns (uint)[20] xmem;
|
||||||
|
x = xmem;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
function two() returns (uint) {
|
||||||
|
if (mutex > 0)
|
||||||
|
return 7;
|
||||||
|
mutex = 1;
|
||||||
|
// If this test fails, it might re-execute this function.
|
||||||
|
x[0]();
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("one()") == encodeArgs(u256(3)));
|
||||||
|
BOOST_CHECK(callContractFunction("two()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(shift_constant_left)
|
BOOST_AUTO_TEST_CASE(shift_constant_left)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
char const* sourceCode = R"(
|
||||||
@ -7553,6 +8265,21 @@ BOOST_AUTO_TEST_CASE(packed_storage_overflow)
|
|||||||
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x1234), u256(0), u256(0), u256(0xfffe)));
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x1234), u256(0), u256(0), u256(0xfffe)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(inline_assembly_invalidjumplabel)
|
||||||
|
{
|
||||||
|
char const* sourceCode = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
assembly {
|
||||||
|
jump(invalidJumpLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
compileAndRun(sourceCode, 0, "C");
|
||||||
|
BOOST_CHECK(callContractFunction("f()") == encodeArgs());
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -325,12 +325,12 @@ BOOST_AUTO_TEST_CASE(arithmetics)
|
|||||||
byte(Instruction::ADD),
|
byte(Instruction::ADD),
|
||||||
byte(Instruction::DUP2),
|
byte(Instruction::DUP2),
|
||||||
byte(Instruction::ISZERO),
|
byte(Instruction::ISZERO),
|
||||||
byte(Instruction::PUSH1), 0x2,
|
byte(Instruction::PUSH1), 0x0,
|
||||||
byte(Instruction::JUMPI),
|
byte(Instruction::JUMPI),
|
||||||
byte(Instruction::MOD),
|
byte(Instruction::MOD),
|
||||||
byte(Instruction::DUP2),
|
byte(Instruction::DUP2),
|
||||||
byte(Instruction::ISZERO),
|
byte(Instruction::ISZERO),
|
||||||
byte(Instruction::PUSH1), 0x2,
|
byte(Instruction::PUSH1), 0x0,
|
||||||
byte(Instruction::JUMPI),
|
byte(Instruction::JUMPI),
|
||||||
byte(Instruction::DIV),
|
byte(Instruction::DIV),
|
||||||
byte(Instruction::MUL)});
|
byte(Instruction::MUL)});
|
||||||
@ -425,39 +425,6 @@ BOOST_AUTO_TEST_CASE(assignment)
|
|||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(function_call)
|
|
||||||
{
|
|
||||||
char const* sourceCode = "contract test {\n"
|
|
||||||
" function f(uint a, uint b) { a += g(a + 1, b) * 2; }\n"
|
|
||||||
" function g(uint a, uint b) returns (uint c) {}\n"
|
|
||||||
"}\n";
|
|
||||||
bytes code = compileFirstExpression(sourceCode, {{"test", "g"}},
|
|
||||||
{{"test", "f", "a"}, {"test", "f", "b"}});
|
|
||||||
|
|
||||||
// Stack: a, b
|
|
||||||
bytes expectation({byte(Instruction::PUSH1), 0x02,
|
|
||||||
byte(Instruction::PUSH1), 0x0c,
|
|
||||||
byte(Instruction::PUSH1), 0x01,
|
|
||||||
byte(Instruction::DUP5),
|
|
||||||
byte(Instruction::ADD),
|
|
||||||
// Stack here: a b 2 <ret label> (a+1)
|
|
||||||
byte(Instruction::DUP4),
|
|
||||||
byte(Instruction::PUSH1), 0x13,
|
|
||||||
byte(Instruction::JUMP),
|
|
||||||
byte(Instruction::JUMPDEST),
|
|
||||||
// Stack here: a b 2 g(a+1, b)
|
|
||||||
byte(Instruction::MUL),
|
|
||||||
// Stack here: a b g(a+1, b)*2
|
|
||||||
byte(Instruction::DUP3),
|
|
||||||
byte(Instruction::ADD),
|
|
||||||
// Stack here: a b a+g(a+1, b)*2
|
|
||||||
byte(Instruction::SWAP2),
|
|
||||||
byte(Instruction::POP),
|
|
||||||
byte(Instruction::DUP2),
|
|
||||||
byte(Instruction::JUMPDEST)});
|
|
||||||
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(negative_literals_8bits)
|
BOOST_AUTO_TEST_CASE(negative_literals_8bits)
|
||||||
{
|
{
|
||||||
char const* sourceCode = "contract test {\n"
|
char const* sourceCode = "contract test {\n"
|
||||||
|
@ -57,7 +57,7 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false,
|
|||||||
{
|
{
|
||||||
sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(source)));
|
sourceUnit = parser.parse(std::make_shared<Scanner>(CharStream(source)));
|
||||||
if(!sourceUnit)
|
if(!sourceUnit)
|
||||||
return make_pair(sourceUnit, nullptr);
|
BOOST_FAIL("Parsing failed in type checker test.");
|
||||||
|
|
||||||
SyntaxChecker syntaxChecker(errors);
|
SyntaxChecker syntaxChecker(errors);
|
||||||
if (!syntaxChecker.checkSyntax(*sourceUnit))
|
if (!syntaxChecker.checkSyntax(*sourceUnit))
|
||||||
@ -1535,6 +1535,21 @@ BOOST_AUTO_TEST_CASE(enum_implicit_conversion_is_not_okay)
|
|||||||
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(enum_to_enum_conversion_is_not_okay)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract test {
|
||||||
|
enum Paper { Up, Down, Left, Right }
|
||||||
|
enum Ground { North, South, West, East }
|
||||||
|
function test()
|
||||||
|
{
|
||||||
|
Ground(Paper.Up);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(enum_duplicate_values)
|
BOOST_AUTO_TEST_CASE(enum_duplicate_values)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
@ -4015,8 +4030,8 @@ BOOST_AUTO_TEST_CASE(calling_payable)
|
|||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
contract receiver { function pay() payable {} }
|
contract receiver { function pay() payable {} }
|
||||||
contract test {
|
contract test {
|
||||||
funciton f() { (new receiver()).pay.value(10)(); }
|
function f() { (new receiver()).pay.value(10)(); }
|
||||||
recevier r = new receiver();
|
receiver r = new receiver();
|
||||||
function g() { r.pay.value(10)(); }
|
function g() { r.pay.value(10)(); }
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
@ -4117,6 +4132,231 @@ BOOST_AUTO_TEST_CASE(using_directive_for_missing_selftype)
|
|||||||
BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
|
BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
function(uint) returns (uint) x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type_parameter)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f(function(uint) external returns (uint) g) returns (function(uint) external returns (uint)) {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type_returned)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() returns (function(uint) external returns (uint) g) {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(private_function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
function(uint) private returns (uint) x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(public_function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
function(uint) public returns (uint) x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(payable_internal_function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function (uint) internal payable returns (uint) x;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(call_value_on_non_payable_function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function (uint) external returns (uint) x;
|
||||||
|
function f() {
|
||||||
|
x.value(2)();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(external_function_type_returning_internal)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function() external returns (function () internal) x;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(external_function_type_taking_internal)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function(function () internal) external x;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(call_value_on_payable_function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function (uint) external payable returns (uint) x;
|
||||||
|
function f() {
|
||||||
|
x.value(2)(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter)
|
||||||
|
{
|
||||||
|
// It should not be possible to give internal functions
|
||||||
|
// as parameters to external functions.
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f(function(uint) internal returns (uint) x) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_returned_from_public_function)
|
||||||
|
{
|
||||||
|
// It should not be possible to return internal functions from external functions.
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() returns (function(uint) internal returns (uint) x) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_internal)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library L {
|
||||||
|
function f(function(uint) internal returns (uint) x) internal {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(internal_function_as_external_parameter_in_library_external)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
library L {
|
||||||
|
function f(function(uint) internal returns (uint) x) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(function_type_arrays)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function(uint) external returns (uint)[] public x;
|
||||||
|
function(uint) internal returns (uint)[10] y;
|
||||||
|
function f() {
|
||||||
|
function(uint) returns (uint)[10] memory a;
|
||||||
|
function(uint) returns (uint)[10] storage b = y;
|
||||||
|
function(uint) external returns (uint)[] memory c;
|
||||||
|
c = new function(uint) external returns (uint)[](200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(delete_function_type)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function(uint) external returns (uint) x;
|
||||||
|
function(uint) internal returns (uint) y;
|
||||||
|
function f() {
|
||||||
|
delete x;
|
||||||
|
var a = y;
|
||||||
|
delete a;
|
||||||
|
delete y;
|
||||||
|
var c = f;
|
||||||
|
delete c;
|
||||||
|
function(uint) internal returns (uint) g;
|
||||||
|
delete g;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(success(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(delete_function_type_invalid)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
delete f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_AUTO_TEST_CASE(delete_external_function_type_invalid)
|
||||||
|
{
|
||||||
|
char const* text = R"(
|
||||||
|
contract C {
|
||||||
|
function f() {
|
||||||
|
delete this.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
BOOST_CHECK(expectError(text, false) == Error::Type::TypeError);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(invalid_fixed_point_literal)
|
BOOST_AUTO_TEST_CASE(invalid_fixed_point_literal)
|
||||||
{
|
{
|
||||||
char const* text = R"(
|
char const* text = R"(
|
||||||
|
@ -45,21 +45,19 @@ public:
|
|||||||
bool _userDocumentation
|
bool _userDocumentation
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
std::string generatedDocumentationString;
|
|
||||||
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing failed");
|
ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parse("pragma solidity >=0.0;\n" + _code), "Parsing failed");
|
||||||
|
|
||||||
if (_userDocumentation)
|
|
||||||
generatedDocumentationString = m_compilerStack.metadata("", DocumentationType::NatspecUser);
|
|
||||||
else
|
|
||||||
generatedDocumentationString = m_compilerStack.metadata("", DocumentationType::NatspecDev);
|
|
||||||
Json::Value generatedDocumentation;
|
Json::Value generatedDocumentation;
|
||||||
m_reader.parse(generatedDocumentationString, generatedDocumentation);
|
if (_userDocumentation)
|
||||||
|
generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecUser);
|
||||||
|
else
|
||||||
|
generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecDev);
|
||||||
Json::Value expectedDocumentation;
|
Json::Value expectedDocumentation;
|
||||||
m_reader.parse(_expectedDocumentationString, expectedDocumentation);
|
m_reader.parse(_expectedDocumentationString, expectedDocumentation);
|
||||||
BOOST_CHECK_MESSAGE(
|
BOOST_CHECK_MESSAGE(
|
||||||
expectedDocumentation == generatedDocumentation,
|
expectedDocumentation == generatedDocumentation,
|
||||||
"Expected " << _expectedDocumentationString <<
|
"Expected " << expectedDocumentation.toStyledString() <<
|
||||||
"\n but got:\n" << generatedDocumentationString
|
"\n but got:\n" << generatedDocumentation.toStyledString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user